WD与地图
给 \(n\) 个点, \(m\) 条边的有向图,每个点有初始点权,要求支持:
删除一条有向边
修改点权
求点 \(x\) 所在强连通分量的前 \(k\) 大点权和。
\(n,m,q \le 2 \times 10^5\)
如果是无向图就好说很多了,时光倒流后并查集维护连通块及权值线段树合并即可。
现在有向边与无向边不同的是,每条边加入后不一定会将 \(u_i,v_i\) 缩成一个 SCC。于是一种想法是,对于每条边,我们二分其两端点被缩成一个 SCC 的时间,用 tarjan 判定,然后再像无向图那样搞。复杂度:\(O(m^2\log q)\),不如暴力。
显然要整体二分。但是每次需要加入 \(0...mid\) 的所有边后 tarjan,复杂度爆炸。这个也好说,我们有可撤销并查集,这样我们就可以让 \(mid\) 任意游动,而只花费大概游动距离那么多的时间。然而可撤销并查集只支持 \(mid\) 左移,如何处理 \(mid\) 右移的复杂度?事实上,我们只需要加 \(ql...qr\) 这么多边。因为这个整体二分比较神奇,其询问和修改是一个东西:询问肯定是时间 \(l\) 之前加入缩不成 SCC 的边,以及时间 \(l...mid\) 中新出现的边,而我们需要加的也恰好是这些。因为之前我们已经用并查集维护缩了一波 SCC,那些“缩不成 SCC 的边”一定被留下来,其余边一定没用。而一层中每条边只会出现一次,因此 tarjan 的复杂度为 \(O(nlogn)\),加上可撤销并查集的维护复杂度,总复杂度为 \(O(nlog^2n)\)。
整体二分完以后就转化成无向图问题做即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define N 401000
#define NN 20001000
typedef long long ll;
template<typename T> inline void read(T &x) {
x = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
}
using namespace std;
const int up = 1e9;
template<typename T> inline void MIN(T &a, T b) {
if (b < a) a = b;
}
template<typename T> inline void MAX(T &a, T b) {
if (a < b) a = b;
}
int n, m, q;
int a[N];
struct Edge{
int u, v, t, fti;//first connected time
}E[N], tl[N], tr[N];
map<int, int> mp[N];
struct Qy {
int opt, x, y;
}qy[N];
inline bool cmp(const Edge &a, const Edge &b) {
return a.fti < b.fti;
}
int fath[N], siz[N];
struct Info {
int x, y;
Info(int xx = 0, int yy = 0) { x = xx, y = yy; }
}info[N << 1];
int top;
int find(int cur) {
return fath[cur] == cur ? cur : find(fath[cur]);
}
inline void merge(int x, int y) {
x = find(x), y = find(y);
if (siz[x] > siz[y]) swap(x, y);
info[++top] = (Info){x, y};
siz[y] += siz[x]; fath[x] = y;
}
inline void del(int tp) {
int x = info[tp].x, y = info[tp].y;
siz[y] -= siz[x]; fath[x] = x;
}
struct edge{
int nxt, to;
}e[N << 1];
int head[N], ecnt;
inline void addedge(int from, int to) {
e[++ecnt] = (edge){head[from], to};
head[from] = ecnt;
}
int dfn[N], low[N], dcnt;
int stk[N], stop;
bool instk[N];
void tarjan(int cur) {
dfn[cur] = low[cur] = ++dcnt;
stk[++stop] = cur; instk[cur] = true;
for (int i = head[cur]; i; i = e[i].nxt) {
int to = e[i].to;
if (!dfn[to]) tarjan(to), MIN(low[cur], low[to]);
else if (instk[to]) MIN(low[cur], dfn[to]);
}
if (low[cur] == dfn[cur]) {
int lst = 0, tmp;
do {
tmp = stk[stop--];
if (!lst) lst = tmp;
else merge(tmp, lst);
instk[tmp] = false;
} while (tmp != cur);
}
}
int htot, h[N << 1];
inline void clear() {
ecnt = dcnt = stop = 0;
for (int i = 1; i <= htot; ++i) {
int p = h[i];
dfn[p] = low[p] = head[p] = 0;
instk[p] = false;
}
htot = 0;
}
void sol(int L, int R, int ql, int qr) {
if (L == R) {
for (int i = ql; i <= qr; ++i) E[i].fti = L;
return ;
}
int mid = (L + R) >> 1;
for (int i = ql; i <= qr; ++i) if (E[i].t <= mid) {
int fu = find(E[i].u), fv = find(E[i].v);
h[++htot] = fu, h[++htot] = fv;
if (fu != fv) addedge(fu, fv);
}
int tmp = top;
for (int i= 1; i <= htot; ++i) if (!dfn[h[i]]) tarjan(h[i]);
int nl = 0, nr = 0;
for (int i = ql; i <= qr; ++i) {
int u = find(E[i].u), v = find(E[i].v);
if (E[i].t <= mid && u == v) tl[++nl] = E[i];
else tr[++nr] = E[i];
}
for (int i = 1; i <= nl; ++i) E[ql + i - 1] = tl[i];
for (int i = 1; i <= nr; ++i) E[ql + nl + i - 1] = tr[i];
clear();
sol(mid + 1, R, ql + nl, qr);
while (top > tmp) del(top), --top;
sol(L, mid, ql, ql + nl - 1);
}
int ls[NN], rs[NN], tsiz[NN], rt[N], ttot;
ll sum[NN], ans[N];
inline void pushup(int cur) {
tsiz[cur] = tsiz[ls[cur]] + tsiz[rs[cur]];
sum[cur] = sum[ls[cur]] + sum[rs[cur]];
}
void add(int L, int R, int pos, int x, int &cur) {
if (!cur) cur = ++ttot;
if (L == R) return tsiz[cur] += x, sum[cur] += L * x, void();
int mid = (L + R) >> 1;
if (pos <= mid) add(L, mid, pos, x, ls[cur]);
else add(mid + 1, R, pos, x, rs[cur]);
pushup(cur);
}
ll query(int L, int R, int k, int cur) {
if (!cur) return 0;
if (L == R) return 1ll * k * L;
int mid = (L + R) >> 1;
int sz = tsiz[rs[cur]];
if (k <= sz) return query(mid + 1, R, k, rs[cur]);//bug
k -= sz; return sum[rs[cur]] + query(L, mid, k, ls[cur]);//bug
}
int SGT_Merge(int L, int R, int x, int y) {
if (!x || !y) return x ^ y;
if (L == R) return sum[x] += sum[y], tsiz[x] += tsiz[y], x;
int mid = (L + R) >> 1;
ls[x] = SGT_Merge(L, mid, ls[x], ls[y]);
rs[x] = SGT_Merge(mid + 1, R, rs[x], rs[y]);
pushup(x);
return x;
}
inline void Merge(int x, int y) {
x = find(x), y = find(y); if (x == y) return ;
if (siz[x] > siz[y]) swap(x, y);
rt[y] = SGT_Merge(1, up, rt[y], rt[x]);
merge(x, y);
}
signed main() {
read(n), read(m), read(q);
for (int i = 1; i <= n; ++i) read(a[i]), fath[i] = i, siz[i] = 1;
for (int i = 1; i <= m; ++i)
read(E[i].u), read(E[i].v), E[i].t = 0, mp[E[i].u][E[i].v] = i;
for (int i = 1; i <= q; ++i) {
int opt, x, y; read(opt), read(x), read(y);
qy[i] = (Qy){opt, x, y};
if (opt == 1) E[mp[x][y]].t = q - i + 1;
if (opt == 2) a[x] += y;//bug
}
sol(0, q + 1, 1, m);
for (int i = 1; i <= n; ++i) add(1, up, a[i], 1, rt[i]);
sort(E + 1, E + 1 + m, cmp);
int ptr = 1;
for (int i = q; i; --i) {
while (ptr <= m && E[ptr].fti <= q - i + 1) Merge(E[ptr].u, E[ptr].v), ++ptr;
int opt = qy[i].opt, x = qy[i].x, y = qy[i].y;
if (opt == 2) {
int fx = find(x);
add(1, up, a[x], -1, rt[fx]);
a[x] -= y;
add(1, up, a[x], 1, rt[fx]);
}
if (opt == 3) {
x = find(x);
if (tsiz[rt[x]] <= y) ans[i] = sum[rt[x]];
else ans[i] = query(1, up, y, rt[x]);
}
}
for (int i = 1; i <= q; ++i) if (qy[i].opt == 3) printf("%lld\n", ans[i]);
return 0;
}