动态图连通性问题(离线/在线)
有这样一类问题(例:loj121,122),要求在无向图中实现如下操作:
1.增加一条边
2.删除一条边
3.询问两点是否连通
这类问题有离线做法和在线做法,两者的思想完全不同
离线做法的思想是,考虑到每一条边存在的时间范围是一个区间,在线段树对应的区间打上标记,然后从根节点自顶向下dfs,每访问一个区间,把该区间上打过标记的所有边都加进去,回溯时再删除,用带撤销并查集维护连通性即可,访问到叶子结点的时候输出答案
复杂度$O(mlogmlogn)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=5e5+10,inf=0x3f3f3f3f; 5 int n,m,fa[N],siz[N],sta[N],tp,sum[N<<2]; 6 struct QR {int f,u,v;} a[N]; 7 struct E { 8 int u,v; 9 bool operator<(const E& b)const {return u!=b.u?u<b.u:v<b.v;} 10 }; 11 vector<E> vec[N<<2]; 12 map<E,int> mp; 13 #define l(u) (u<<1) 14 #define r(u) (u<<1|1) 15 #define mid ((l+r)>>1) 16 void pu(int u) {sum[u]=sum[l(u)]+sum[r(u)];} 17 void build(int u=1,int l=1,int r=m) { 18 if(l==r) {sum[u]=(a[l].f==2); return;} 19 build(l(u),l,mid),build(r(u),mid+1,r),pu(u); 20 } 21 void upd(int L,int R,E x,int u=1,int l=1,int r=m) { 22 if(l>=L&&r<=R) {vec[u].push_back(x); return;} 23 if(l>R||r<L)return; 24 upd(L,R,x,l(u),l,mid),upd(L,R,x,r(u),mid+1,r); 25 } 26 int fd(int x) {return fa[x]?fd(fa[x]):x;} 27 void mg(int x,int y) { 28 if((x=fd(x))==(y=fd(y)))return; 29 if(siz[x]>siz[y])swap(x,y); 30 fa[x]=y,siz[y]+=siz[x]; 31 sta[++tp]=x; 32 } 33 void sp(int x) {siz[fa[x]]-=siz[x],fa[x]=0;} 34 void dfs(int u=1,int l=1,int r=m) { 35 if(!sum[u])return; 36 int now=tp; 37 for(E x:vec[u])mg(x.u,x.v); 38 if(l==r) { 39 puts(fd(a[l].u)==fd(a[l].v)?"Y":"N"); 40 for(; tp>now; --tp)sp(sta[tp]); 41 return; 42 } 43 dfs(l(u),l,mid),dfs(r(u),mid+1,r); 44 for(; tp>now; --tp)sp(sta[tp]); 45 } 46 47 int main() { 48 scanf("%d%d",&n,&m); 49 for(int i=1; i<=n; ++i)fa[i]=0,siz[i]=1; 50 for(int i=1; i<=m; ++i) { 51 scanf("%d%d%d",&a[i].f,&a[i].u,&a[i].v); 52 if(a[i].u>a[i].v)swap(a[i].u,a[i].v); 53 } 54 for(int i=1; i<=m; ++i) { 55 E x= {a[i].u,a[i].v}; 56 if(a[i].f==0)mp[x]=i; 57 else if(a[i].f==1) { 58 int l=mp[x],r=i-1; 59 upd(l,r,x); 60 mp.erase(x); 61 } 62 } 63 for(auto t:mp) { 64 E x=t.first; 65 int l=t.second,r=m; 66 upd(l,r,x); 67 } 68 mp.clear(); 69 build(); 70 dfs(); 71 return 0; 72 }
在线做法有一个叫做Holm-de Lichtenberg-Thorup的算法,比较复杂,其基本思想是动态维护生成树,如果生成树上的边被删除了,那么就需要寻找可以替代的边。
寻找替代边的过程可以表示为:假设生成树上的某条边被删除,使得原生成树被分割成两棵子树F,F',假设siz[F]<siz[F'],那么就从F的结点中连出的边里寻找连向F'的(显然,每一条边的两个端点,要么全在F中,要么一个在F中一个在F'中)。为了加速这个过程,需要给每条边赋予一个优先级,每次寻找重连边时优先级高的首先考虑。初始优先级为0,如果在F中寻找到某条边不能够连向F',那么就把这条边的优先级+1,由于每条边优先级+1必然伴随着结点数量减半,因此每条边最多被考虑O(logn)次。可以用带子树查询的LCT维护各种信息
复杂度$O(mlog^2n)$,常数巨大
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=5e3+10,M=1e9+7; 5 int n,m; 6 #define l(u) ch[u][0] 7 #define r(u) ch[u][1] 8 struct HDLT { 9 static const int N=5e3+10; 10 int blocks; 11 ll f(int x,int y) {return (ll)x*M+y;} 12 struct LCT { 13 struct List { 14 int hd[N],pre[N],nxt[N]; 15 void init(int n) {for(int u=1; u<=n; ++u)hd[u]=0;} 16 void link(int u,int v) { 17 if(!hd[u])hd[u]=pre[v]=nxt[v]=v; 18 else { 19 int x=hd[u],y=pre[x]; 20 pre[x]=nxt[y]=v; 21 pre[v]=y,nxt[v]=x; 22 } 23 } 24 void cut(int u,int v) { 25 if(nxt[v]==v)hd[u]=0; 26 else { 27 if(hd[u]==v)hd[u]=nxt[v]; 28 pre[nxt[v]]=pre[v],nxt[pre[v]]=nxt[v]; 29 } 30 } 31 bool empty(int u) {return hd[u]==0;} 32 int first(int u) {return hd[u];} 33 } chv[2]; 34 unordered_set<int> G[N][2]; 35 void linkv(int u,int v) { 36 if(!u||!v)return; 37 sizv[u]+=siz[v]; 38 for(int i=0; i<2; ++i)if(tag[v][i])chv[i].link(u,v); 39 } 40 void cutv(int u,int v) { 41 if(!u||!v)return; 42 sizv[u]-=siz[v]; 43 for(int i=0; i<2; ++i)if(tag[v][i])chv[i].cut(u,v); 44 } 45 int fa[N],ch[N][2],flp[N],sta[N],tp,siz[N],sizv[N],tag[N][2]; 46 void rev(int u) {flp[u]^=1,swap(l(u),r(u));} 47 void pu(int u) { 48 if(!u)return; 49 for(int i=0; i<2; ++i)tag[u][i]=tag[l(u)][i]|tag[r(u)][i]|!G[u][i].empty()|!chv[i].empty(u); 50 siz[u]=siz[l(u)]+siz[r(u)]+1+sizv[u]; 51 } 52 void pd(int u) {if(flp[u])rev(l(u)),rev(r(u)),flp[u]=0;} 53 int sf(int u) {return u==r(fa[u]);} 54 bool isrt(int u) {return u!=l(fa[u])&&u!=r(fa[u]);} 55 void rot(int u) { 56 int v=fa[u],f=sf(u); 57 bool flag=isrt(v); 58 if(!flag)ch[fa[v]][sf(v)]=u; 59 else if(fa[v])cutv(fa[v],v); 60 ch[v][f]=ch[u][f^1],fa[ch[v][f]]=v; 61 fa[u]=fa[v],ch[u][f^1]=v,fa[v]=u,pu(v); 62 if(flag)pu(u),linkv(fa[u],u); 63 } 64 void splay(int u) { 65 sta[tp=0]=u; 66 for(int v=u; !isrt(v); v=fa[v])sta[++tp]=fa[v]; 67 for(; ~tp; pd(sta[tp--])); 68 for(; !isrt(u); rot(u))if(!isrt(fa[u])&&sf(fa[u])==sf(u))rot(fa[u]); 69 } 70 void access(int u) { 71 int w=u; 72 for(int v=0; u; u=fa[v=u])splay(u),linkv(u,r(u)),cutv(u,v),r(u)=v,pu(u); 73 splay(w); 74 } 75 void makert(int u) {access(u),rev(u);} 76 void join(int u,int v) {makert(u),access(v);} 77 int findrt(int u) {access(u); for(; l(u); pd(u),u=l(u)); splay(u); return u;} 78 void link(int u,int v) { 79 makert(u); 80 if(findrt(v)==u)return; 81 fa[u]=v,linkv(v,u),pu(v),access(v); 82 } 83 void cut(int u,int v) {join(u,v); if(l(v)!=u||r(u))return; fa[u]=l(v)=0,pu(v);} 84 int get(int u,int f) { 85 access(u); 86 if(!tag[u][f])return 0; 87 while(G[u][f].empty()) { 88 if(tag[l(u)][f])u=l(u); 89 else if(tag[r(u)][f])u=r(u); 90 else u=chv[f].first(u); 91 } 92 return u; 93 } 94 bool isconnected(int u,int v) {return findrt(u)==findrt(v);} 95 void ins(int f,int u,int v) { 96 if(G[u][f].size()==0)access(u); 97 G[u][f].insert(v),pu(u); 98 if(G[v][f].size()==0)access(v); 99 G[v][f].insert(u),pu(v); 100 } 101 void del(int f,int u,int v) { 102 if(G[u][f].size()==1)access(u); 103 G[u][f].erase(v),pu(u); 104 if(G[v][f].size()==1)access(v); 105 G[v][f].erase(u),pu(v); 106 } 107 void init(int n) { 108 for(int i=0; i<2; ++i) { 109 chv[i].init(n); 110 for(int u=1; u<=n; ++u)G[u][i].clear(); 111 } 112 for(int u=1; u<=n; ++u)fa[u]=l(u)=r(u)=tag[u][0]=tag[u][1]=flp[u]=sizv[u]=0,siz[u]=1; 113 } 114 } F[20]; 115 unordered_map<ll,int> LV; 116 void instree(int lv,int u,int v) {LV[f(u,v)]=LV[f(v,u)]=lv; F[lv].ins(0,u,v);} 117 void insgraph(int lv,int u,int v) {LV[f(u,v)]=LV[f(v,u)]=lv; F[lv].ins(1,u,v);} 118 void deltree(int lv,int u,int v) {F[lv].del(0,u,v);} 119 void delgraph(int lv,int u,int v) {F[lv].del(1,u,v);} 120 bool findreplace(int lv,int u,int v) { 121 F[lv].access(u),F[lv].access(v); 122 if(F[lv].siz[u]>F[lv].siz[v])swap(u,v); 123 int t=u,replacev=0; 124 while((u=F[lv].get(u,0))) { 125 unordered_set<int>& G=F[lv].G[u][0]; 126 while(G.size()) { 127 int v=*G.begin(); 128 deltree(lv,u,v),instree(lv+1,u,v),F[lv+1].link(u,v); 129 } 130 } 131 u=t; 132 while((u=F[lv].get(u,1))) { 133 unordered_set<int>& G=F[lv].G[u][1]; 134 while(G.size()) { 135 int v=*G.begin(); 136 if(F[lv].isconnected(u,v))delgraph(lv,u,v),insgraph(lv+1,u,v); 137 else { 138 replacev=v,delgraph(lv,u,replacev),instree(lv,u,replacev); 139 for(int i=0; i<=lv; ++i)F[i].link(u,replacev); 140 break; 141 } 142 } 143 if(replacev)return 1; 144 } 145 return 0; 146 } 147 int isconnected(int u,int v) {return F[0].isconnected(u,v);} 148 int getblocksize(int u) {F[0].access(u); return F[0].siz[u];} 149 void link(int u,int v) { 150 if(!isconnected(u,v))F[0].link(u,v),instree(0,u,v),--blocks; 151 else insgraph(0,u,v); 152 } 153 void cut(int u,int v) { 154 int lv=LV[f(u,v)]; 155 if(F[lv].G[u][0].count(v)) { 156 for(int i=0; i<=lv; ++i)F[i].cut(u,v); 157 deltree(lv,u,v); 158 ++blocks; 159 for(int i=lv; i>=0; --i)if(findreplace(i,u,v)) {--blocks; break;} 160 } else delgraph(lv,u,v); 161 } 162 void del(int u) { 163 vector<int> vec; 164 for(int t=0; t<2; ++t) 165 for(int i=0; i<20; ++i) 166 for(int v:F[i].G[u][t]) 167 vec.push_back(v); 168 for(int v:vec)cut(u,v); 169 --blocks; 170 } 171 void init(int n) { 172 for(int i=0; i<20; ++i)F[i].init(n); 173 LV.clear(),blocks=n; 174 } 175 } solver; 176 int main() { 177 scanf("%d%d",&n,&m); 178 solver.init(n); 179 for(int lastans=0; m--;) { 180 int f,u,v; 181 scanf("%d%d%d",&f,&u,&v); 182 u^=lastans,v^=lastans; 183 if(f==0)solver.link(u,v); 184 else if(f==1)solver.cut(u,v); 185 else if(f==2) { 186 int t=solver.isconnected(u,v); 187 printf("%c\n",t?'Y':'N'); 188 lastans=(t?u:v); 189 } 190 } 191 return 0; 192 }