洛谷P5163 WD与地图
只有洛谷的毒瘤才会在毒瘤月赛里出毒瘤题......
题意:三个操作,删边,改变点权,求点x所在强连通分量内前k大点权之和。
解:狗屎毒瘤数据结构乱堆......
整体二分套(tarjan+并查集) + 线段树合并。
首先可以变成加边。
然后就是神奇操作让人难以置信......
对于每条边,我们有个时刻t使得它的两端点在同一scc内。那么如何求出这个t呢?
整体二分!...这又是怎么想到的啊......
对于一个时刻mid,我们把小于它的边提取出来缩点,如果有的边两端点在一个scc,那么它的t就不大于mid。否则大于mid。
为了保证整体二分的复杂度,我们每次操作tarjan的图不能太大。具体来说,把之前做过的区间的scc用并查集缩起来。
对于每条边,提取两端点所在scc的代表元为关键点,构出虚图(?????),然后tarjan。
之后递归左边,之后并查集缩点,之后递归右边。
所有t求出之后,再倒着跑一遍询问,按照时刻把边的两端点在并查集中合并(表示成为一个scc),并对权值线段树进行合并。
查询就是所在scc的权值线段树的前k大之和,这个好办。
注意爆int。我写了300行7k......
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <map> typedef long long LL;
const int N = , M = , V = ; struct Edge {
int nex, v;
}edge[M]; int tp; struct Node {
int t, x, y, id;
}node[M], t1[M], t2[M]; struct Ask {
int f, x, y;
LL ans;
}ask[M]; std::vector<Node> v[M];
std::stack<int> S;
std::map<int, int> mp[N];
int e[N], fr[N], stk[N], top, dfn[N], low[N], use[N], X[N + M], xx, rt[N], sum[V], ls[V], rs[V], val[N];
int Time, scc_cnt, num, n, m, q, tot;
LL Val[V]; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} namespace ufs {
int fa[N];
int find(int x) {
if(fa[x] == x) {
return x;
}
return fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
fa[find(x)] = find(y);
return;
}
inline void clear() {
for(int i = ; i <= n; i++) {
fa[i] = i;
}
return;
}
} void tarjan(int x) {
low[x] = dfn[x] = ++num;
S.push(x);
//printf("tarjan %d \n", x);
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(!dfn[y]) {
tarjan(y);
low[x] = std::min(low[x], low[y]);
}
else if(!fr[y]) { // find
low[x] = std::min(low[x], dfn[y]);
}
}
if(low[x] == dfn[x]) {
//printf("getscc \n");
++scc_cnt;
int y;
do {
y = S.top();
//printf("%d ", y);
S.pop();
fr[y] = scc_cnt;
//ufs::merge(x, y);
} while(y != x);
//puts("");
}
return;
} inline void TAR() {
scc_cnt = num = ;
for(int i = ; i <= top; i++) {
if(!dfn[stk[i]]) {
tarjan(stk[i]);
}
}
return;
} inline void link(int x, int y) {
x = ufs::find(x); y = ufs::find(y);
if(use[x] != Time) {
use[x] = Time;
dfn[x] = fr[x] = e[x] = ;
stk[++top] = x;
}
if(use[y] != Time) {
use[y] = Time;
dfn[y] = fr[y] = e[y] = ;
stk[++top] = y;
}
add(x, y); // self-circle is OK
return;
} inline void solve(int L, int R, int l, int r) {
if(R < L) {
return;
}
//printf("solve %d %d %d %d \n", L, R, l, r);
if(l == r) {
for(int i = L; i <= R; i++) {
v[r].push_back(node[i]);
}
return;
}
int mid = (l + r) >> ;
++Time; top = tp = ;
for(int i = L; i <= R; i++) {
if(node[i].t <= mid) {
link(node[i].x, node[i].y);
}
}
//int ufs_t = ufs::top;
TAR();
int top1 = , top2 = ;
for(int i = L; i <= R; i++) {
int x = ufs::find(node[i].x), y = ufs::find(node[i].y);
if(node[i].t <= mid && fr[x] == fr[y]) {
t1[++top1] = node[i];
}
else {
t2[++top2] = node[i];
}
}
memcpy(node + L, t1 + , top1 * sizeof(Node));
memcpy(node + L + top1, t2 + , top2 * sizeof(Node));
solve(L, L + top1 - , l, mid);
for(int i = L; i < L + top1; i++) {
int x = ufs::find(node[i].x);
int y = ufs::find(node[i].y);
ufs::merge(x, y);
}
solve(L + top1, R, mid + , r);
return;
} int merge(int x, int y) {
if(!x || !y || x == y) {
return x | y;
}
int o = ++tot;
sum[o] = sum[x] + sum[y];
Val[o] = Val[x] + Val[y];
ls[o] = merge(ls[x], ls[y]);
rs[o] = merge(rs[x], rs[y]);
return o;
} void add(int p, int v, int l, int r, int &o) {
//printf("add : %d %d %d %d \n", p, v, l, r);
if(!o) {
o = ++tot;
}
if(l == r) {
sum[o] += v;
Val[o] += v * X[r];
return;
}
int mid = (l + r) >> ;
if(p <= mid) {
add(p, v, l, mid, ls[o]);
}
else {
add(p, v, mid + , r, rs[o]);
}
sum[o] = sum[ls[o]] + sum[rs[o]];
Val[o] = Val[ls[o]] + Val[rs[o]];
return;
} LL query(int k, int l, int r, int o) {
//printf("query %d [%d %d] \n", k, l, r);
if(!o) {
return ;
}
if(l == r) {
return 1ll * std::min(k, sum[o]) * X[r];
}
int mid = (l + r) >> ;
if(sum[rs[o]] >= k) {
return query(k, mid + , r, rs[o]);
}
else {
return Val[rs[o]] + query(k - sum[rs[o]], l, mid, ls[o]);
}
} int main() { scanf("%d%d%d", &n, &m, &q);
ufs::clear();
for(int i = ; i <= n; i++) {
scanf("%d", &val[i]);
X[++xx] = val[i];
}
for(int i = ; i <= m; i++) {
scanf("%d%d", &node[i].x, &node[i].y);
mp[node[i].x][node[i].y] = i;
node[i].id = i;
}
for(int i = ; i <= q; i++) {
scanf("%d%d%d", &ask[i].f, &ask[i].x, &ask[i].y);
if(ask[i].f == ) {
ask[i].y += val[ask[i].x];
X[++xx] = ask[i].y;
std::swap(val[ask[i].x], ask[i].y);
}
}
std::sort(X + , X + xx + );
xx = std::unique(X + , X + xx + ) - X - ;
for(int i = ; i <= n; i++) {
val[i] = std::lower_bound(X + , X + xx + , val[i]) - X;
}
for(int i = ; i <= q; i++) {
if(ask[i].f == ) {
ask[i].y = std::lower_bound(X + , X + xx + , ask[i].y) - X;
}
}
std::reverse(ask + , ask + q + );
for(int i = ; i <= q; i++) {
if(ask[i].f == ) {
int t = mp[ask[i].x][ask[i].y];
node[t].t = i;
}
} solve(, m, , q + );
//printf("--------------------------------\n");
ufs::clear();
for(int i = ; i <= n; i++) {
add(val[i], , , xx, rt[i]);
}
for(int i = ; i <= q; i++) {
//printf("i = %d \n", i);
for(int j = ; j < v[i].size(); j++) {
//printf(" > edge = %d %d \n", v[i][j].x, v[i][j].y);
int x = ufs::find(v[i][j].x), y = ufs::find(v[i][j].y);
ufs::merge(x, y);
int t = ufs::find(x);
rt[t] = merge(rt[x], rt[y]);
//printf("%d = merge %d %d \n", t, x, y);
}
if(ask[i].f == ) {
int t = ufs::find(ask[i].x);
ask[i].ans = query(ask[i].y, , xx, rt[t]);
}
else if(ask[i].f == ) {
int t = ufs::find(ask[i].x);
//printf("change %d : %d -> %d \n", ask[i].x, X[val[ask[i].x]], X[ask[i].y]);
add(val[ask[i].x], -, , xx, rt[t]);
add(ask[i].y, , , xx, rt[t]);
val[ask[i].x] = ask[i].y;
}
}
for(int i = q; i >= ; i--) {
if(ask[i].f == ) {
printf("%lld\n", ask[i].ans);
}
}
return ;
}
AC代码
对拍真是个好东西。
洛谷P5163 WD与地图的更多相关文章
- 题解 洛谷 P5163 【WD与地图】
首先将操作倒序,把删边转化为加边.先考虑若边是无向边,条件为连通,要怎么处理. 可以用并查集来维护连通性,对每个连通块维护一颗权值线段树,连通块的合并用线段树合并来实现,线段树同时也支持了修改点权. ...
- 洛谷 P5162 WD与积木 解题报告
P5162 WD与积木 题目背景 WD整日沉浸在积木中,无法自拔-- 题目描述 WD想买\(n\)块积木,商场中每块积木的高度都是\(1\),俯视图为正方形(边长不一定相同).由于一些特殊原因,商家会 ...
- 洛谷P5159 WD与矩阵
题目背景 WD整日沉浸在矩阵中,无法自拔-- 题目描述 WD特别喜欢矩阵,尤其是\(01\)矩阵. 一天,CX给了WD一个巨大的\(n\)行\(m\)列的\(01\)矩阵,WD发现这个矩阵每行.每列的 ...
- 洛谷P5162 WD与积木 [DP,NTT]
传送门 思路 真是非常套路的一道题-- 考虑\(DP\):设\(f_n\)为\(n\)个积木能搭出的方案数,\(g_n\)为所有方案的高度之和. 容易得到转移方程: \[ \begin{align*} ...
- 洛谷 P5162 WD与积木【多项式求逆】
设f[i]为i个积木能堆出来的种类,g[i]为i个积木能堆出来的种类和 \[ f[n]=\sum_{i=1}^{n}C_{n}^{i}g[n-i] \] \[ g[n]=\sum_{i=1}^{n}C ...
- P5163 WD与地图(整体二分+权值线段树)
传送门 细节要人命.jpg 这题思路太新奇了--首先不难发现可以倒着做变成加边,但是它还需要我们资瓷加边的同时维护强连通分量.显然加边之后暴力跑是不行的 然后有一个想法,对于一条边\((u,v)\), ...
- 洛谷P5160 WD与循环
我们看这段代码 int cnt = 0; for (int a_1 = 0; a_1 <= m; a_1++) { for (int a_2 = 0; a_1 + a_2 <= m; a_ ...
- P5163 WD与地图 [整体二分,强连通分量,线段树合并]
首先不用说,倒着操作.整体二分来做强连通分量,然后线段树合并,这题就做完了. // powered by c++11 // by Isaunoya #include <bits/stdc++.h ...
- 关于三目运算符与if语句的效率与洛谷P2704题解
题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图.在每一格平原地形上最 ...
随机推荐
- 【数据库】Mysql中主键的几种表设计组合的实际应用效果
写在前面 前前后后忙忙碌碌,度过了新工作的三个月.博客许久未新,似乎对忙碌没有一点点防备.总结下来三个月不断的磨砺自己,努力从独乐乐转变到众乐乐,体会到不一样的是,连办公室的新玩意都能引起莫名的兴趣了 ...
- LVM : 简介
在对磁盘分区的大小进行规划时,往往不能确定这个分区要使用的空间的大小.而使用 fdisk.gdisk 等工具对磁盘分区后,每个分区的大小就固定了.如果分区设置的过大,就白白浪费了磁盘空间:如果分区设置 ...
- v-for v-if || v-else
<el-col> <div v-for="item in resultDetail" class="physical-content" v-i ...
- linux书籍
<鸟哥私房菜-基础版> <实战LINUX_SHELL编程与服务器管理> <LINUX命令行与SHELL脚本编程大全第2版].布卢姆.扫描版> <Linux初学 ...
- dxteam团队项目终审报告
一. 团队成员的简介和个人博客地址 M1阶段 http://www.cnblogs.com/dxteam/p/3991514.html M2阶段 新成员 邓亚梅 http://www.cnblogs. ...
- Week 2 代码审查
我的伙伴是6班的小伙子潘礼鹏,经过几天的相处我觉得真的是说话很有趣的人,性格非常好,我们很划得来. 以下为我对他的代码的审查结果: VS2012与VS2013的兼容性 在这里写一个工具集的问题,不同的 ...
- 《linux内核设计与实现》第四章
调度程序负责决定哪个进程投入运行,何时运行以及运行多长时间.只有通过调度程序合理调度,系统资源才能最大限度发挥作用,多进程才会有并发执行的效果. 最大限度地利用处理器时间的原则是,只要有可以执行的进程 ...
- Linux内核分析——进程的描述和进程的创建
进程的描述和进程的创建 一. 进程的描述 (一)进程控制块PCB——task_struct 1.操作系统的三大管理功能包括: (1)进程管理 (2)内存管理 (3)文件系统 2.PCB task_st ...
- 20145221 《Java程序设计》实验报告四:Android开发基础
20145221 <Java程序设计>实验报告四:Android开发基础 实验要求 基于Android Studio开发简单的Android应用并部署测试; 了解Android组件.布局管 ...
- HDOJ2025_查找最大元素
一道简单题 HDOJ2025_查找最大元素 #include<stdio.h> #include<stdlib.h> #include<ctype.h> #incl ...