动态图连通性问题(离线/在线)

有这样一类问题(例: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 }
View Code

在线做法有一个叫做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 }
View Code

 

posted @ 2021-04-04 18:20  jrltx  阅读(895)  评论(0编辑  收藏  举报