Description
给出n个点(编号1~n)和m条无向边,支持两个操作
保证任何时刻任何两点间有至少一条路径可达,且连接任何两点的边至多有一条
1 < N < 30000 1 < M < 100000
Solution
我们知道在无向图的一个双连通子图中没有割点,即子图中任意两点间都有一条以上路径连接
这道题看起来像动态求点双连通分量??
往往加边比删边容易,我们考虑反向解决问题。
对于一棵树,如果在两个节点间再连一条无向边,就会与树上两点间的路径一起构成环,
成为双连通分量的一部分,也就是这条路径上所有点都不可能成为必经边。
这给我们提供了解题思路:
构建一棵树,树上边的初始权值为 1 ,每加一条边,就把两端点树上路径上的每条边权值设为 0 ,询问时统计权值和
显然可以用边树剖(边树剖对lca的处理简直了orz)
Code
#include <cstdio> #include <cstdlib> #include <map> using namespace std; const int N=3e4+10,M=2e5+10; bool used[M],cut[M]; int head[N],to[M],nxt[M],from[M],b_tot=1;//边 int n,m,a,b,q[M][3],Q,ans[M];//输入 int dfn[N],son[N],si[N],t_tot,top[N],dep[N],fa[N];//树剖 map <int,int> dict; struct node { int lc,rc,l,r,sum; bool delay; }f[N*2]; int rt,s_tot; void dfs1(int u,int fx) { dep[u]=dep[fx]+1; fa[u]=fx,si[u]=1; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(si[v] || cut[i]) continue; used[i]=true; dfs1(v,u),si[u]+=si[v]; if(!son[u] || si[son[u]]<si[v]) son[u]=v; } } void dfs2(int u) { dfn[u]=++t_tot; if(son[u]) top[son[u]]=top[u],dfs2(son[u]); else return ; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(!used[i] || v==son[u]) continue; top[v]=v,dfs2(v); } } void push_down(int g) { if(!f[g].delay) return ; int lc=f[g].lc,rc=f[g].rc; f[lc].delay=f[rc].delay=true; f[lc].sum=f[rc].sum=0; f[g].delay=false; } void push_up(int g) { f[g].sum=f[f[g].lc].sum+f[f[g].rc].sum; } void build(int &g,int l,int r) { g=++s_tot; f[g].l=l,f[g].r=r; if(l==r) { if(l!=1) f[g].sum=1; else f[g].sum=0; return ; } int mid=(l+r)>>1; build(f[g].lc,l,mid); build(f[g].rc,mid+1,r); push_up(g); } void add(int x,int y) { to[++b_tot]=y,from[b_tot]=x,nxt[b_tot]=head[x],head[x]=b_tot; dict[x*n+y]=dict[y*n+x]=b_tot; to[++b_tot]=x,from[b_tot]=y,nxt[b_tot]=head[y],head[y]=b_tot; } void change(int g,int l,int r) { if(f[g].delay) return; if(f[g].l>=l && f[g].r<=r) { f[g].delay=true,f[g].sum=0; return; } int mid=(f[g].l+f[g].r)>>1; if(r<=mid) change(f[g].lc,l,r); else if(l>mid) change(f[g].rc,l,r); else change(f[g].lc,l,mid),change(f[g].rc,mid+1,r); push_up(g); } int get(int g,int l,int r) { if(f[g].l>=l && f[g].r<=r) return f[g].sum; push_down(g); int mid=(f[g].l+f[g].r)>>1; if(r<=mid) return get(f[g].lc,l,r); else if(l>mid) return get(f[g].rc,l,r); else return get(f[g].lc,l,mid)+get(f[g].rc,mid+1,r); } int Get(int x,int y) { if(x==y) return 0; int ans=0; int px=top[x],py=top[y]; while(px!=py) if(dep[px]>=dep[py]) { ans+=get(rt,dfn[px],dfn[x]); x=fa[px],px=top[x]; } else { ans+=get(rt,dfn[py],dfn[y]); y=fa[py],py=top[y]; } if(dfn[x]<dfn[y]) ans+=get(rt,dfn[x]+1,dfn[y]); if(dfn[y]<dfn[x]) ans+=get(rt,dfn[y]+1,dfn[x]); return ans; } void Change(int x,int y) { if(x==y) return; int px=top[x],py=top[y]; while(px!=py) if(dep[px]>=dep[py]) { change(rt,dfn[px],dfn[x]); x=fa[px],px=top[x]; } else { change(rt,dfn[py],dfn[y]); y=fa[py],py=top[y]; } if(dfn[x]<dfn[y]) change(rt,dfn[x]+1,dfn[y]); if(dfn[y]<dfn[x]) change(rt,dfn[y]+1,dfn[x]); } int main() { scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d",&a,&b),add(a,b); while(1) { scanf("%d",&q[++Q][0]); if(q[Q][0]==-1) break; scanf("%d%d",&q[Q][1],&q[Q][2]); if(q[Q][0]==0) { int bh=dict[q[Q][1]*n+q[Q][2]]; cut[bh]=cut[bh^1]=true; } } Q--; dfs1(1,0); top[1]=1,dfs2(1); build(rt,1,t_tot); for(int i=2;i<=b_tot;i+=2) if(!cut[i] && !used[i] && !used[i^1]) Change(from[i],to[i]); for(int i=Q;i;i--) { if(q[i][0]==0) Change(q[i][1],q[i][2]); else ans[++ans[0]]=Get(q[i][1],q[i][2]); } for(int i=ans[0];i;i--) printf("%d\n",ans[i]); return 0; }
原文:https://www.cnblogs.com/hsez-cyx/p/12342943.html