【题解】P5163 WD与地图 (这题极好)
题目大意
初始有一个有向图,现在要你支持以下操作
- 删除一条边
- 把一个点点权修改下
- 输出一个点所在的SCC的最大k个点权和
\(n\le 1e5,m,q \le 2e5,\)
删边不好处理,先时间倒流下,问题变成了支持加入一条边。
如果图是无向图,这题就很好做(并查集+treap/线段树合并),因为每条边都把两个端点立马变成了一个SCC了。而有向图难处理就难在可能很多条边会在最后一条关键边加入时,同时使得很多点变成一个SCC。
考虑我们让"很多边"和"关键边"同时加入,这样显然是不影响答案的(SCC没有改变)。
图的连通性是满足二分性的,考虑对于每条边,二分出这样一个最早时刻\(t\),使得这条边的两个端点在一个SCC里。然后我们在\(t\)时刻时把这条边当做无向边加入。
这样的复杂度无法接受,然而多个元素共用一个\(\rm{check}\)可以整体二分。可撤销并查集维护SCC即可。
得到这些之后,再套用无向图的做法即可
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std; typedef long long ll;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c)) f|=c==45,c=getchar();
while( isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=2e5+5;
int n,m,q,w[maxn],T[maxn],usd[maxn],len;
pair<int,int> e[maxn];
struct{int op,a,b;}que[maxn];
ll ans[maxn];
vector<int>sav;
int getVal(int x){
return lower_bound(sav.begin(),sav.end(),x)-sav.begin();
}
namespace getTime{
namespace BCJ{
int r[maxn],siz[maxn],stk[maxn],top;
int Find(int x){return x==r[x]?x:Find(r[x]);}
int Merge(int x,int y){
x=Find(x),y=Find(y);
if(x==y) return 0;
if(siz[x]>siz[y]) swap(x,y);
r[x]=y; siz[y]+=siz[x]; stk[++top]=x;
return 1;
}
void Undo(int cnt){
int temp;
while(cnt-->0&&top)
temp=stk[top--],siz[r[temp]]-=siz[temp],r[temp]=temp;
}
void init(){for(int t=1;t<=n;++t)r[t]=t,siz[t]=1;}
}using namespace BCJ;
namespace Tarjan{
vector<int>e[maxn],ve;
int dfn[maxn],low[maxn],T,stk[maxn],in[maxn],top,cnt;
void add(int fr,int to){
if(fr==to) return;
ve.push_back(fr); ve.push_back(to);
e[fr].push_back(to);
}
void clear(){
for(auto t:ve) e[t].clear(),dfn[t]=low[t]=0;
ve.clear(); T=0; cnt=0;
}
void dfs(int now){
dfn[now]=low[now]=++T; stk[++top]=now; in[now]=1;
for(auto t:e[now]){
if(!dfn[t]) dfs(t),low[now]=min(low[now],low[t]);
if(dfn[t]&&in[t]) low[now]=min(low[now],dfn[t]);
}
if(low[now]==dfn[now]){
int temp;
do
cnt+=Merge(temp=stk[top--],now),in[temp]=0;
while(temp!=now);
}
}
int init(){
for(auto t:ve)
if(!dfn[t])
dfs(t);
return cnt;
}
}
vector<pair<int,int>> getEdge(int l,int r){
vector< pair<int,int> > ret;
if(l==0){
for(int t=1;t<=m;++t)
if(!usd[t])
ret.push_back(e[t]);
}
for(int t=l;t<=r;++t)
if(que[t].op==1)
ret.push_back({que[t].a,que[t].b});
return ret;
}
void solve(int l,int r,vector<int>E){
if(l>=r||E.empty()){
for(auto t:E) T[t]=l;
return;
}
int mid=(l+r)>>1,cnt=0;
Tarjan::clear();
for(auto t:E)
if(usd[t]<=mid)
Tarjan::add(Find(e[t].first),Find(e[t].second));
cnt=Tarjan::init();
vector<int>lef,rgt;
for(auto t:E)
if(Find(e[t].first)==Find(e[t].second)&&usd[t]<=mid)
lef.push_back(t);
else rgt.push_back(t);
solve(mid+1,r,rgt);
Undo(cnt);
solve(l,mid,lef);
}
void init(){
BCJ::init();
vector<int>ve;
for(int t=1;t<=m;++t) ve.push_back(t);
solve(0,q+1,ve);
}
}
namespace getAns{
struct EDGE{
int u,v,T;
bool operator < (EDGE x)const{return T<x.T;}
}edge[maxn];
struct NODE{
int ls,rs,val,cnt,siz;
}seg[maxn<<6];
ll sum[maxn<<6];
int cnt,r[maxn],rt[maxn];
void pp(int pos){
seg[pos].siz=seg[pos].cnt+seg[seg[pos].ls].siz+seg[seg[pos].rs].siz;
sum[pos]=sum[seg[pos].ls]+sum[seg[pos].rs]+1ll*seg[pos].val*seg[pos].cnt;
}
#define mid ((l+r)>>1)
#define lef l,mid,seg[pos].ls
#define rgt mid+1,r,seg[pos].rs
void build(int p,int l,int r,int&pos){
if(l>p||r<p) return;
if(!pos) pos=++cnt;
if(l==r) return sum[pos]=sav[p],seg[pos].val=sav[p],seg[pos].cnt=seg[pos].siz=1,void();
if(p<=mid) build(p,lef);
if(p>mid) build(p,rgt);
pp(pos);
}
void upd(int p,int delta,int l,int r,int&pos){
if(p<l||r<p) return;
if(!pos) pos=++cnt;
if(l==r){
seg[pos].cnt+=delta; seg[pos].siz+=delta;
seg[pos].val=sav[p];
sum[pos]=1ll*seg[pos].cnt*seg[pos].val;
return;
}
if(p<=mid) upd(p,delta,lef);
else upd(p,delta,rgt);
pp(pos);
}
int Merge(int l,int r,int t1,int t2){
if(!t1||!t2) return t1|t2;
if(t1==t2) cerr<<"Merge_same"<<t1<<endl;
if(seg[t1].val!=seg[t2].val) return cerr<<"wrong value!"<<endl,0;
seg[t1].cnt+=seg[t2].cnt;
seg[t1].siz+=seg[t2].siz;
seg[t1].ls=Merge(l,mid,seg[t1].ls,seg[t2].ls);
seg[t1].rs=Merge(mid+1,r,seg[t1].rs,seg[t2].rs);
pp(t1);
return t1;
}
ll sumK(int k,int l,int r,int pos){
if(!pos||!k) return 0;
if(l==r) return 1ll*seg[pos].val*min(seg[pos].cnt,k);
if(k>=seg[seg[pos].rs].siz) return sum[seg[pos].rs]+sumK(k-seg[seg[pos].rs].siz,lef);
return sumK(k,rgt);
}
int Find(int x){return x==r[x]?x:r[x]=Find(r[x]);}
#undef mid
#undef lef
#undef rgt
void Merge(int u,int v){
u=Find(u); v=Find(v);
if(u==v) return;
rt[v]=rt[u]=Merge(1,len,rt[u],rt[v]);
r[u]=v;
}
void init(){
cnt=n;
for(int t=1;t<=n;++t) build(getVal(w[t]),1,len,rt[t]),r[t]=t;
for(int t=1;t<=m;++t)
edge[t]={e[t].first,e[t].second,T[t]};
sort(edge+1,edge+m+1);
for(int t=1,j=0;t<=q;++t){
if(que[t].op==1) continue;
while(j+1<=m&&edge[j+1].T<=t) ++j,Merge(edge[j].u,edge[j].v);
if(que[t].op==2)
upd(getVal(w[que[t].a]),-1,1,len,rt[Find(que[t].a)]),w[que[t].a]-=que[t].b,upd(getVal(w[que[t].a]),1,1,len,rt[Find(que[t].a)]);
if(que[t].op==3) ans[t]=sumK(que[t].b,1,len,rt[Find(que[t].a)]);
}
}
}
signed main(){
n=qr(); m=qr(); q=qr();
for(int t=1;t<=n;++t) w[t]=qr(),sav.push_back(w[t]);
for(int t=1;t<=m;++t) e[t].first=qr(),e[t].second=qr();
sort(e+1,e+m+1);
for(int t=1;t<=q;++t) {
que[t].op=qr(),que[t].a=qr(),que[t].b=qr();
if(que[t].op==2) w[que[t].a]+=que[t].b,sav.push_back(w[que[t].a]);
}
reverse(que+1,que+q+1);
for(int t=1;t<=q;++t){
if(que[t].op==1) usd[lower_bound(e+1,e+m+1,(pair<int,int>){que[t].a,que[t].b})-e]=t;
}
sav.push_back(-1);
sort(sav.begin(),sav.end());
sav.resize(unique(sav.begin(),sav.end())-sav.begin());
len=sav.size()-1;
getTime::init();
getAns::init();
for(int t=1;t<=q;++t)
if(que[q-t+1].op==3)
printf("%lld\n",ans[q-t+1]);
return 0;
}