[DS记录]P5163 WD与地图

· · 个人记录

题意 : 给出一张有向图,要求支持下列操作 :

允许离线。

------------ 这个 $\rm idea$ 应该最初是 $\bf{d}{\color{red}uls}$ 的。 删边维护 $\rm SCC$ ,一脸不可做的样子,考虑时光倒流,变成加边。 仍然不好直接做,考虑求出每条边被“缩起来”的时间。 如果能求出这个,维护强连通性就和无向图连通性一样简单了……剩余的操作就是线段树合并了。 发现“缩起来”是具有单调性的,可考虑**整体二分**。 先考虑朴素的判定,不难想到将出现时间 $\leq mid$ 的边全部建立,然后缩点。 此时若某条边两端在同一个强连通分量内,则其被缩起来的时间 $\leq mid$。 接下来改进为整体二分。 我们希望能够得到 $\leq mid$ 的边集的缩点情况,似乎并没有显然的做法。 先不考虑复杂度,观察一下这个算法的行为。 每次分治时,会将当前边集分成左右两部分,其中左半部满足已经缩起来。 这样,我们处理右半部时,左边的所有边的贡献就只有强连通性,没有所谓的 $\rm DAG$ 边。 换句话说,整体二分会把缩不起来的 $\rm DAG$ 边丢到后面继续缩。 这样,我们就只需要传递连通性。 可以只用并查集维护 $[1,l)$ 中边的强连通性,然后加入 $[l,mid]$ 内的边来跑。 具体流程就是 : - 把 $[l,mid]$ 的边加入缩好强连通性的图中。 - 跑 `tarjan` 来缩点,为了保证复杂度,注意不要遍历没有边相连的点。 - 把边划分为两个部分。 - 将新产生的强连通性加入并查集。 - 将边集清空。 - 求解右儿子。 - 将本层产生的强连通性从并查集中撤回。 - 求解左儿子。 复杂度 $O(n\log^2n)$。 `1h`写,`30min`调,居然是最优解…… ```cpp #include<algorithm> #include<cstdio> #include<map> #define ll long long #define Pr pair<int,int> #define mp make_pair #define MaxN 100500 #define MaxM 200500 using namespace std; int read(){ int X=0;char ch=0; while(ch<48||ch>57)ch=getchar(); while(ch>=48&&ch<=57)X=X*10+(ch^48),ch=getchar(); return X; } struct UFS { int f[MaxN],c[MaxN],sx[MaxN],sy[MaxN],tot; void Init(int n) {for (int i=1;i<=n;i++){f[i]=i;c[i]=1;}} int find(int u) {return f[u]==u ? u : find(f[u]);} void merge(int x,int y){ x=find(x);y=find(y); if (x==y)return ; if (c[x]>c[y])swap(x,y); c[f[x]=y]+=c[x]; sx[++tot]=x;sy[tot]=y; } void undo(int lim) { while(tot>lim){ int x=sx[tot],y=sy[tot]; c[y]-=c[f[x]=x]; tot--; } } }F; struct sLine{int f,t,p;}l[MaxM],sl[MaxM]; struct Line{int t,nxt;}g[MaxM]; int tl,fir[MaxN],st[MaxN],tot,vis[MaxN],vf; void adl(int f,int t){ if (vis[f]<vf)vis[st[++tot]=f]=vf; if (vis[t]<vf)vis[st[++tot]=t]=vf; g[++tl]=(Line){t,fir[f]};fir[f]=tl; } int dfn[MaxN],low[MaxN],stk[MaxN],top,tim; bool in[MaxN]; void clear(){ for (int i=1;i<=tot;i++) fir[st[i]]=dfn[st[i]]=low[st[i]]=0; tl=tot=tim=0; } void dfs(int u) { low[u]=dfn[u]=++tim; in[stk[++top]=u]=1; for (int i=fir[u],v;i;i=g[i].nxt) if (dfn[v=g[i].t]){ if (in[v])low[u]=min(low[u],dfn[v]); }else {dfs(v);low[u]=min(low[u],low[v]);} if (low[u]==dfn[u]){ while(stk[top]!=u){ F.merge(stk[top],stk[top-1]); in[stk[top--]]=0; }in[stk[top--]]=0; } } int n; void solve(int tl,int tr,int pl,int pr) { if (pl>pr)return ; if (tl==tr){ for (int i=pl;i<=pr;i++)l[i].p=tl; return ; }int mid=(tl+tr)>>1,savt=F.tot; vf++; for (int i=pl;i<=pr;i++) if (l[i].p<=mid) adl(F.find(l[i].f),F.find(l[i].t)); for (int i=1;i<=tot;i++) if (!dfn[st[i]])dfs(st[i]); clear(); int pmid=pl-1,nr=0; for (int i=pl;i<=pr;i++) if (F.find(l[i].f)==F.find(l[i].t)&&l[i].p<=mid) l[++pmid]=l[i]; else sl[++nr]=l[i]; for (int i=1;i<=nr;i++)l[pmid+i]=sl[i]; solve(mid+1,tr,pmid+1,pr); F.undo(savt); solve(tl,mid,pl,pmid); } struct Node {int l,r,c;ll s;}a[MaxN*60]; int to,wfc,tn; const int lim=1000000000; void add(int l,int r,int &u) { if (!u)u=++tn; a[u].c+=wfc; if (l==r){a[u].s+=wfc*l;return ;} int mid=(l+r)>>1; if (to<=mid)add(l,mid,a[u].l); else add(mid+1,r,a[u].r); a[u].s=a[a[u].l].s+a[a[u].r].s; } int merge(int x,int y) { if (!x||!y)return x|y; a[x].c+=a[y].c; a[x].s+=a[y].s; if (a[x].l||a[x].r){ a[x].l=merge(a[x].l,a[y].l); a[x].r=merge(a[x].r,a[y].r); }return x; } ll qry(int l,int r,int u,int k) { if (l==r)return 1ll*min(k,a[u].c)*l; int mid=(l+r)>>1,rc=a[a[u].r].c; if (k<=rc)return qry(mid+1,r,a[u].r,k); return qry(l,mid,a[u].l,k-rc)+a[a[u].r].s; } bool cmpL(const sLine &A,const sLine &B) {return A.p<B.p;} struct Data{int op,a,b;}b[MaxM]; map<Pr,int> tpl; int m,q,x[MaxN],rt[MaxN]; ll ans[MaxM];int na; int main() { n=read();m=read();q=read(); for (int i=1;i<=n;i++)x[i]=read(); for (int i=1;i<=m;i++){ l[i].f=read();l[i].t=read(); tpl[mp(l[i].f,l[i].t)]=i; } for (int i=1;i<=q;i++){ b[i].op=read(); b[i].a=read(); b[i].b=read(); if (b[i].op==2) x[b[i].a]+=b[i].b; }reverse(b+1,b+q+1); for (int i=1;i<=q;i++) if (b[i].op==1) l[tpl[mp(b[i].a,b[i].b)]].p=i; F.Init(n); solve(0,q+1,1,m); sort(l+1,l+m+1,cmpL); for (int i=1;i<=n;i++){ to=x[i];wfc=1; add(0,lim,rt[i]); } for (int i=0,p=1;i<=q;i++){ while(p<=m&&l[p].p==i){ int u=F.find(l[p].f),v=F.find(l[p].t); if (u!=v){ int srt=merge(rt[u],rt[v]); F.merge(u,v); rt[F.find(u)]=srt; }p++; }if (b[i].op==2){ int u=F.find(b[i].a); to=x[b[i].a];wfc=-1;add(0,lim,rt[u]); to=(x[b[i].a]-=b[i].b);wfc=1;add(0,lim,rt[u]); }if (b[i].op==3){ int u=F.find(b[i].a); ans[++na]=qry(0,lim,rt[u],b[i].b); } }for (int i=na;i;i--)printf("%lld\n",ans[i]); return 0; } ```