版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/85623390
【题目】
原题地址
给定一幅有向图,支持三种操作:
- 删去一条边
- 给一个点权值增加
- 求
点所在强连通分量权值前
大的和。
【解题思路】
我好像不是很会做啊(其实是自己s*了)。
首先倒序操作,那么删边可以变成加边。一个
的做法是我们对每个点维护一棵权值线段树,每次增加一条边
,我们就从
向
跑缩边,将全部权值线段树合并一下。询问就在权值线段树上二分即可。
这样做的复杂度是
的。
实际上瓶颈在于无法快速知道哪条边在什么时候被缩起来,否则我们可以按时间进行线段树合并。
考虑到边被缩起来的时间满足可二分性,于是我们用整体二分的思想:将出现时间 的边全部加入图中跑缩边,我们可以知道在 时间内有哪些边被缩掉。同时我们还需要用可持久化数组(雾)来维护一下是否在一个强连通分量内以及 (这样不用重复遍历边了)。每条边在每个分治深度下贡献一次,那么复杂度就是 了。
这种知识点题的思考过程:欸我要一个tarjan,码码码;欸我要一个线段树合并,码码码;欸我要一个整体二分,码码码。然后就200+行了。
【参考代码】
#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=4e5+10,M=N*50;
int tn,n,m,Q,cnt,val[N];
vector<ll>ans;
set<pii>E;
ll res[N];
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(ll x)
{
if(x>9) write(x/10);
putchar(x%10^48);
}
void writeln(ll x){write(x);puts("");}
}
using namespace IO;
namespace Segment
{
int sz,ls[M],rs[M],cnt[M],rt[N];
ll sum[M];
void update(int &x,int l,int r,int p,int v)
{
if(!x)x=++sz;cnt[x]+=v;sum[x]+=(ll)p*v;
if(l==r) return;
int mid=(l+r)>>1;
if(p<=mid) update(ls[x],l,mid,p,v);
else update(rs[x],mid+1,r,p,v);
}
ll query(int x,int l,int r,int p)
{
if(p>=cnt[x]) return sum[x];
if(l==r) return sum[x]/cnt[x]*p;
int mid=(l+r)>>1;
if(cnt[rs[x]]>=p) return query(rs[x],mid+1,r,p);
else return query(ls[x],l,mid,p-cnt[rs[x]])+sum[rs[x]];
}
int Tmerge(int x,int y)
{
if(!x || !y) return x+y;
cnt[x]+=cnt[y];sum[x]+=sum[y];
ls[x]=Tmerge(ls[x],ls[y]);
rs[x]=Tmerge(rs[x],rs[y]);
return x;
}
}
using Segment::update;
using Segment::query;
using Segment::Tmerge;
using Segment::rt;
namespace DSU
{
int fa[N];
int findf(int x){return fa[x]==x?x:fa[x]=findf(fa[x]);}
void merge(int x,int y)
{
x=findf(x);y=findf(y);
if(x==y) return;
if(x>y) swap(x,y);
fa[x]=y;
}
}
using namespace DSU;
namespace Graph
{
int ind,tot;
int low[N],dfn[N],ins[N],head[N];
stack<int>st;
struct edge
{
int u,v,t;
edge(int _u=0,int _v=0,int _t=0):u(_u),v(_v),t(_t){}
}g[N],cpy[N];
vector<edge>mp[N];
struct Tway
{
int v,nex;
Tway(int _v=0,int _nex=0):v(_v),nex(_nex){}
}e[N];
void addedge(int u,int v){e[++tot]=Tway(v,head[u]);head[u]=tot;}
void clear(int x){low[x]=dfn[x]=ins[x]=0;}
void tarjan(int x)
{
dfn[x]=low[x]=++ind;ins[x]=1;st.push(x);
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
else if(ins[v]) low[x]=min(low[x],dfn[v]);
}
if(low[x]^dfn[x]) return;
for(int t=0;t^x;t=st.top(),st.pop()) merge(st.top(),x),ins[st.top()]=0;
}
}
using Graph::addedge;
using Graph::tarjan;
using Graph::dfn;
using Graph::edge;
using Graph::mp;
using Graph::g;
using Graph::cpy;
namespace Divide_Conquer
{
pii arr[N];
void solve(int l,int r,int L,int R)
{
//cerr<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
if(l==r)
{
for(int i=L;i<=R;++i) mp[l].pb(g[i]);
return;
}
if(L==R)
{
mp[(findf(g[L].u)==findf(g[L].v))?g[L].t:Q+1].pb(g[L]);
return;
}
int mid=(l+r)>>1,c=0;
for(int i=L;i<=R;++i) if(g[i].t<=mid)
{
int u=findf(g[i].u),v=findf(g[i].v);
arr[++c]=mkp(u,v);Graph::head[u]=Graph::head[v]=0;
}
Graph::tot=0;
for(int i=1;i<=c;++i)
{
int u=arr[i].fi,v=arr[i].se;addedge(u,v);
Graph::clear(u);Graph::clear(v);
}
//cerr<<c<<endl;
//if(r==12500) exit(0);
Graph::ind=0;
while(!Graph::st.empty()) Graph::st.pop();
for(int i=1;i<=c;++i)
{
if(!dfn[arr[i].fi]) tarjan(arr[i].fi);
if(!dfn[arr[i].se]) tarjan(arr[i].se);
}
int tl=0,tr=L,t=1;vector<pii> cp;
for(int i=L;i<=R;++i)
{
int fg=0;
if(g[i].t<=mid)
{
int u=arr[t].fi,v=arr[t].se;++t;
if(findf(u)==findf(v)) fg=1;
cp.pb(mkp(u,fa[u]));cp.pb(mkp(v,fa[v]));
}
if(fg) g[tr++]=g[i]; else cpy[++tl]=g[i];
}
for(int i=tr;i<=R;++i) g[i]=cpy[i-tr+1];
for(int i=1;i<=c;++i) fa[arr[i].fi]=arr[i].fi,fa[arr[i].se]=arr[i].se;
if(L<tr) solve(l,mid,L,tr-1);
for(vector<pii>::iterator it=cp.begin();it!=cp.end();++it) fa[(*it).fi]=(*it).se;
//exit(0);
if(tr<=R) solve(mid+1,r,tr,R);
}
}
struct Tquery
{
int t,a,b;
Tquery(int _t=0,int _a=0,int _b=0):t(_t),a(_a),b(_b){}
}qs[N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("LGP5163.in","r",stdin);
freopen("LGP5163.out","w",stdout);
#endif
n=read();m=read();Q=read();
for(int i=1;i<=n;++i) val[i]=read(),fa[i]=i;
for(int i=1,u,v;i<=m;++i) u=read(),v=read(),E.insert(mkp(u,v));
for(int i=Q;i;--i)
{
int t=read(),a=read(),b=read();
qs[i]=Tquery(t,a,b);
if(t==1) g[++cnt]=edge(a,b,i),E.erase(mkp(a,b));
else if(t==2) val[a]+=b;
}
for(set<pii>::iterator it=E.begin();it!=E.end();++it) g[++cnt]=edge((*it).fi,(*it).se,0);
Divide_Conquer::solve(0,Q+1,1,m);
for(int i=1;i<=n;++i) tn=max(tn,val[fa[i]=i]);
for(int i=1;i<=n;++i) update(rt[i],1,tn,val[i],1);
qs[0].t=1;
for(int i=0;i<=Q;++i)
{
if(qs[i].t==1)
{
for(vector<edge>::iterator it=mp[i].begin();it!=mp[i].end();++it)
{
int u=(*it).u,v=(*it).v;
u=findf(u);v=findf(v);
if(u==v) continue;
fa[v]=u;rt[u]=Tmerge(rt[u],rt[v]);
}
}
else if(qs[i].t==2)
{
int x=findf(qs[i].a),&v=val[qs[i].a];
update(rt[x],1,tn,v,-1);update(rt[x],1,tn,v-=qs[i].b,1);
}
else ans.pb(query(rt[findf(qs[i].a)],1,tn,qs[i].b));
}
for(int i=(int)ans.size()-1;~i;--i) writeln(ans[i]);//printf("%lld\n",ans[i]);
return 0;
}