P5163-WD与地图【tarjan,整体二分,线段树合并】
正題
題目鏈接:https://www.luogu.com.cn/problem/P5163
題目大意
給出nnn個點mmm條有向邊,點有權值,要求支持操作
1≤n≤105,1≤m,q≤2×1051\leq n\leq 10^5,1\leq m,q\leq 2\times 10^51≤n≤105,1≤m,q≤2×105
解題思路
首先刪邊肯定是時光倒流改成加邊,然后考慮怎么繼續做。
我們需要處理一些點集什么時候合并,這樣的合并其實不會超過n?1n-1n?1次。
而且每次肯定是合并某條邊(x,y)(x,y)(x,y)兩個點所在的強連通分量,但是每次加入的一條邊(x,y)(x,y)(x,y)不一定會即使生效。
我們可以考慮對于每條邊求出它在后來加入哪條邊加入之后生效了,這個可以考慮整體二分,我們每次把所有詢問的邊集在[0,mid][0,mid][0,mid]區間的邊加入然后跑tarjantarjantarjan,把跑出來的強連通分量縮成一個點然后繼續丟到右邊跑,跑完右邊的子區間之后再撤銷這次tarjantarjantarjan縮起來的點然后跑左邊。
這樣一定是對的因為如果一條答案不在這個區間的邊生效,那么它要不就在之前被合并了要么在這個區間內都合并不了,所以沒有作用。
這個要用一個可撤銷并查集,記得安秩合并就好了。
之后我們就有一個操作變成合并兩個集合了,線段樹合并做剩下的部分就行了
時間復雜度O(nlog?2n)O(n\log^2 n)O(nlog2n)(反正差不多同級就不寫這么詳細了)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<stack> #define ll long long #define mp(x,y) make_pair(x,y) using namespace std; const ll N=8e5+10; struct node{ll to,next; }a[N]; struct enode{ll x,y,t,T; }e[N],p1[N],p2[N]; struct qnode{ll x,k; }q[N]; struct snode{ll x,y,fa,dep; }st[N]; ll n,m,t,tot,snt,clt,cnt,s[N]; ll ls[N],fa[N],dep[N],cl[N]; ll dfn[N],low[N],rt[N],ans[N]; bool ins[N];stack<ll> S; map<pair<ll,ll> ,ll> emp; void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return; } ll find(ll x) {return (fa[x]==x)?x:find(fa[x]);} ll Find(ll x) {return (fa[x]==x)?x:(fa[x]=Find(fa[x]));} void unionn(ll x,ll y){x=find(x);y=find(y);if(x==y)return;if(dep[x]>dep[y])swap(x,y);st[++snt]=(snode){x,y,fa[x],dep[y]};fa[x]=y;dep[y]=max(dep[y],dep[x]+1); } void clearto(ll z){while(snt>z){dep[st[snt].y]=st[snt].dep;fa[st[snt].x]=st[snt].fa;snt--;}return; } void tarjan(ll x){dfn[x]=low[x]=++cnt;S.push(x);ins[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(!dfn[y]){tarjan(y);low[x]=min(low[x],low[y]);}else if(ins[y])low[x]=min(low[x],dfn[y]);}if(low[x]==dfn[x]){while(S.top()!=x){unionn(S.top(),x);ins[S.top()]=0;S.pop();}ins[x]=0;S.pop();}return; } void solve(ll l,ll r,ll L,ll R){if(L>R)return;if(l==r){for(ll i=L;i<=R;i++)e[i].T=l;return;}ll mid=(l+r)>>1,zt=snt;for(ll i=L;i<=R;i++)if(e[i].t<=mid){ll x=find(e[i].x),y=find(e[i].y);addl(x,y);cl[++clt]=x;cl[++clt]=y;}cnt=tot=0;for(ll i=1;i<=clt;i++)if(!dfn[cl[i]])tarjan(cl[i]);ll t1=0,t2=0;for(ll i=L;i<=R;i++){ll x=find(e[i].x),y=find(e[i].y);if(x==y)p1[++t1]=e[i];else p2[++t2]=e[i];}for(ll i=1;i<=t1;i++)e[L+i-1]=p1[i];for(ll i=1;i<=t2;i++)e[L+t1+i-1]=p2[i];while(clt)ls[cl[clt]]=dfn[cl[clt]]=low[cl[clt]]=0,clt--;solve(mid+1,r,L+t1,R);clearto(zt);solve(l,mid,L,L+t1-1);return; } bool cmp(enode x,enode y) {return x.t<y.t;} struct SegTree{ll cnt,w[N<<5],s[N<<5],ls[N<<5],rs[N<<5];void Change(ll &x,ll L,ll R,ll pos,ll val){if(!x)x=++cnt;w[x]+=val;s[x]+=pos*val;if(L==R)return;ll mid=(L+R)>>1;if(pos<=mid)Change(ls[x],L,mid,pos,val);else Change(rs[x],mid+1,R,pos,val);return;}ll Ask(ll x,ll L,ll R,ll k){if(k>=w[x])return s[x];if(L==R)return L*k;ll mid=(L+R)>>1;if(w[rs[x]]>=k)return Ask(rs[x],mid+1,R,k);return s[rs[x]]+Ask(ls[x],L,mid,k-w[rs[x]]);}ll Merge(ll x,ll y){if(!x||!y)return x+y;w[x]=w[x]+w[y];s[x]=s[x]+s[y];ls[x]=Merge(ls[x],ls[y]);rs[x]=Merge(rs[x],rs[y]);return x;} // ll Merge(ll x,ll y,ll L,ll R){ // if(!x||!y)return x+y; // w[x]=w[x]+w[y]; // if(L==R)return x; // ll mid=(L+R)>>1; // ls[x]=Merge(ls[x],ls[y],L,mid); // rs[x]=Merge(rs[x],rs[y],mid+1,R); // return x; // } }T; void Merge(ll x,ll y){x=Find(x),y=Find(y);if(x==y)return;rt[x]=T.Merge(rt[x],rt[y]);fa[y]=x;return; } signed main() {scanf("%lld%lld%lld",&n,&m,&t);for(ll i=1;i<=n;i++)scanf("%lld",&s[i]),fa[i]=i;for(ll i=1;i<=m;i++){scanf("%lld%lld",&e[i].x,&e[i].y);emp[mp(e[i].x,e[i].y)]=i;}for(ll i=t;i>=1;i--){ll op,x,y;scanf("%lld%lld%lld",&op,&x,&y);if(op==1)e[emp[mp(x,y)]].t=i;else if(op==2)q[i].x=-x,q[i].k=y,s[x]+=y;else if(op==3)q[i].x=x,q[i].k=y;}sort(e+1,e+1+m,cmp);solve(0,t+1,1,m);ll z=1;for(ll i=1;i<=n;i++)fa[i]=i,T.Change(rt[i],1,1e9,s[i],1);for(ll i=1;i<=t;i++){while(z<=m&&e[z].T<=i)Merge(e[z].x,e[z].y),z++;if(q[i].x<0){ll x=-q[i].x,w=q[i].k,f=Find(x);T.Change(rt[f],1,1e9,s[x],-1);s[x]-=w;T.Change(rt[f],1,1e9,s[x],1);}else if(q[i].x>0){ll x=Find(q[i].x),k=q[i].k;ans[i]=T.Ask(rt[x],1,1e9,k);}}for(ll i=t;i>=1;i--)if(ans[i])printf("%lld\n",ans[i]);return 0; }總結
以上是生活随笔為你收集整理的P5163-WD与地图【tarjan,整体二分,线段树合并】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二级路由器怎么设置如何设置二级路由器
- 下一篇: qq屏蔽此人消息怎么取消