WD与地图题解
神仙题……有整体二分,线段树合并qwq。
首先要将删边改为加边,倒叙处理操作。
我们要使用整体二分处理一条边是在什么时候才让边的两端在同一个强连通分量里。对于每一次solve,我们传入二分的两端和包含需要处理的边的 vector 数组。将在 mid 前出现的边建出一张图,然后在上面跑一遍 tarjan 。然后将 vector 内已经在同一个强连通分量里并且出现早于 mid 的边丢到左区间,其余丢到右区间进行处理。
每次跑 tarjan 的时候进行缩点,将在一个强连通分量内的点使用可撤销并查集维护到一起。即相当于一个强连通分量使用他们的祖先来替代。跑完 tarjan 后将并查集保留,图删除。容易发现丢到左区间处理的边进行缩点之后其实只是一个点集。所以并不会出现漏掉强连通分量的问题。
处理完这些后与询问一起排序处理,遇到边则并查集连边,当然这里是不必须使用可撤销并查集的,并且合并并查集的同时要进行线段树合并,来维护保存了并查集内元素的权值线段树,使用动态开点。查询的时候使用线段树上二分即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
#define int long long
using namespace std;
int read()
{
int a = 0,x = 1;char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
return a*x;
}
const int N=1e6+7;
int n,m,q,s[N],ans[N];
struct edge{
int u,v,tim;
}E[N];
struct quer{
int op,a,b,tim;
}Q[N],cal[N];
map<pair<int,int>,int>mp;
struct DSU{
int fa[N],siz[N],top,stk[N];
void init(int n) {for(int i = 1;i <= n;i ++) fa[i] = i,siz[i] = 1;}
int find(int s) {return fa[s] == s ? s : find(fa[s]);}
void merge(int a,int b)
{
a = find(a),b = find(b);
if(a == b) return ;
if(siz[a] > siz[b]) swap(a,b);
fa[a] = b,siz[b] += siz[a];
stk[++top] = a;
}
void del(int cur)
{
while(top > cur) {
siz[fa[stk[top]]] -= siz[stk[top]];
fa[stk[top]] = stk[top];--top;
}
}
}uni;
struct TARJAN{
int head[N],go[N],nxt[N],cnt;
int low[N],dfn[N],ins[N],stk[N],top;
vector<int>point;
void add(int u,int v) {
if(!head[u]) point.push_back(u);
point.push_back(v);
go[++cnt] = v;nxt[cnt] = head[u];
head[u] = cnt;
}
void clear() {
for(int u:point) ins[u] = dfn[u] = low[u] = head[u] = 0;cnt = 0;
point.clear();top = 0;
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
stk[++top] = u,ins[u] = 1;
for(int e = head[u];e;e = nxt[e]) {
int v = go[e];
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u],low[v]);
} else if(ins[v]) low[u] = min(low[u],dfn[v]);
}
if(dfn[u] == low[u]) {
do{
uni.merge(u,stk[top]);
ins[stk[top]] = 0;
}while(stk[top--] != u);
}
}
void work() {cnt=0;for(int i:point) if(!dfn[i]) tarjan(i);}
}tar;
void solve(int l,int r,vector<int>d)
{
// printf("l=%d,r=%d\n",l,r);
if(d.empty()) return ;
if(l == r) {for(int u:d) E[u].tim = l;return ;}
int mid = l+r>>1,cur = uni.top;
for(int i:d) {
if(E[i].tim <= mid) tar.add(uni.find(E[i].u),uni.find(E[i].v));
}
tar.work(); tar.clear();
vector<int>dd1,dd2;
for(int i:d) {
int u = uni.find(E[i].u),v = uni.find(E[i].v);
if(u == v && E[i].tim <= mid) dd1.push_back(i);
else dd2.push_back(i);
}
solve(mid+1,r,dd2);uni.del(cur);solve(l,mid,dd1);
}
struct SBT{
int tre[N<<3],ls[N<<3],rs[N<<3],tot,rt[N],siz[N<<3];
void modify(int &root,int l,int r,int p,int x)
{
if(!root) root = ++tot;int mid = l+r>>1;
if(l == r) {tre[root] += x*p,siz[root] += x;return ;}
if(p <= mid) modify(ls[root],l,mid,p,x);
else modify(rs[root],mid+1,r,p,x);
tre[root] = tre[ls[root]] + tre[rs[root]];
siz[root] = siz[ls[root]] + siz[rs[root]];
}
int merge(int a,int b)
{
if(!a || !b) return a+b;
tre[a] += tre[b];siz[a] += siz[b];
ls[a] = merge(ls[a],ls[b]),rs[a] = merge(rs[a],rs[b]);
return a;
}
int query(int root,int l,int r,int x)
{
if(l == r) return min(x,siz[root])*l;int mid = l+r>>1;
if(x == siz[rs[root]]) return tre[rs[root]];
if(x < siz[rs[root]]) return query(rs[root],mid+1,r,x);
if(x > siz[rs[root]]) return tre[rs[root]] + query(ls[root],l,mid,x-siz[rs[root]]);
}
void Merge(int a,int b) {
rt[uni.find(a)] = merge(rt[a],rt[b]);
}
void modify(int root,int x,int op) {
modify(rt[root],1,(int)(1e9),x,op);
}
}ds;
bool cmp(quer a,quer b) {return a.tim!=b.tim?a.tim < b.tim:a.op<b.op;}
signed main()
{
// freopen("random.in","r",stdin);
// freopen("sol.out","w",stdout);
n = read(),m = read(),q = read();uni.init(n);
for(int i = 1;i <= n;i ++) s[i] = read();
for(int i = 1;i <= m;i ++) {
E[i].u = read(),E[i].v = read();
mp[make_pair(E[i].u,E[i].v)] = i;
}
for(int i = 1;i <= q;i ++) {
Q[i].op = read(),Q[i].a = read(),Q[i].b = read(),Q[i].tim = q-i+1;
if(Q[i].op == 1) {
E[mp[make_pair(Q[i].a,Q[i].b)]].tim = q-i+1;
} else if(Q[i].op == 2) s[Q[i].a] += Q[i].b;
}
vector<int>dd;
for(int i = 1;i <= m;i ++) dd.push_back(i);
solve(0,q+1,dd);int tot = m;//return 0;
for(int i = 1;i <= m;i ++) {
cal[i].op = 1,cal[i].tim = E[i].tim,cal[i].a = E[i].u,cal[i].b = E[i].v;
}
for(int i = 1;i <= q;i ++) {
if(Q[i].op == 1) continue;
cal[++tot] = Q[i];
}
for(int i = 1;i <= n;i ++) ds.modify(i,s[i],1);
sort(cal+1,cal+1+tot,cmp);uni.init(n);
for(int i = 1;i <= tot;i ++) {
if(cal[i].op == 1) {
int a = uni.find(cal[i].a),b = uni.find(cal[i].b);
uni.merge(a,b);if(a != b) ds.Merge(a,b);
} else if(cal[i].op == 2) {
int a = uni.find(cal[i].a),b = cal[i].a;
ds.modify(a,s[b],-1);
s[b] -= cal[i].b;
ds.modify(a,s[b],1);
} else {
int a = uni.find(cal[i].a),b = cal[i].b;
ans[i] = ds.query(ds.rt[a],1,(int)(1e9),b);
}
}
for(int i = tot;i >= 1;i --) if(ans[i]) printf("%lld\n",ans[i]);
return 0;
}