[luogu5344]逛森林
由于没有删边操作,可以先建出整棵森林,之后再用并查集判断是否连通,若连通必然与最后的森林相同
但如果用树链剖分+线段树的形式来优化建图,更具体如下:
建立两颗线段树,左边从儿子连向父亲,右边从父亲连向儿子,再将右边线段树上的连向左边对应的点,那么复杂度为$o(m\log^{3}n)$(前两个$\log$为边数,最后一个$\log$是求最短路)
考虑倍增去建立,由于可以重复建立,用ST表的方法去做即可,这样每一条链只对应于至多4个点(上下各两个),即边数为$o(m)$
还有一个问题,就是如何建立倍增所新增的点之间的边,这个并没有线段树的结构那么简单,如果暴力来说,即对于每一个点,向所有包含其的倍增的链连边,这样的边数是$o(n^{2})$的
下面考虑这样一种方式:对于每一个长度不小于2的倍增的链(即不为一个点),向组成其的两条链连边即可,不难发现此时对于一个点以及包含其的倍增的链,这条链的两部分中总(恰好)有一个部分包含该点,由此归纳即可证明这样的连边与前面暴力等价,这一部分的边数降为$o(n\log n)$
总复杂度为$o(m\log n+n\log^{2}n)$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 50005 4 #define M 1000005 5 #define oo 0x3f3f3f3f 6 #define pii pair<int,int> 7 #define mp make_pair 8 struct Edge{ 9 int nex,to,len; 10 }edge[(N<<6)+(M<<4)]; 11 struct op{ 12 int p,x1,y1,x2,y2,w; 13 }a[M]; 14 vector<int>vl,vr,v[N]; 15 priority_queue<pii >q; 16 int V,E,n,m,s,fa[N],dep[N],f[N][16],head[N<<5],vis[N<<5],d[N<<5]; 17 int find(int k){ 18 if (k==fa[k])return k; 19 return fa[k]=find(fa[k]); 20 } 21 bool check(int x,int y){//1表示不相连 22 return find(x)!=find(y); 23 } 24 void merge(int x,int y){ 25 fa[find(x)]=find(y); 26 } 27 int id(int k,int x){ 28 return (k-1)*16+x+1; 29 } 30 int lca(int x,int y){ 31 if (dep[x]<dep[y])swap(x,y); 32 for(int i=15;i>=0;i--) 33 if (dep[f[x][i]]>=dep[y])x=f[x][i]; 34 if (x==y)return x; 35 for(int i=15;i>=0;i--) 36 if (f[x][i]!=f[y][i]){ 37 x=f[x][i]; 38 y=f[y][i]; 39 } 40 return f[x][0]; 41 } 42 void div(int x,int y,vector<int>&v){ 43 int p=15; 44 while ((1<<p)>dep[x]-dep[y]+1)p--; 45 v.push_back(id(x,p)); 46 if (dep[x]-dep[y]+1==(1<<p))return; 47 int d=dep[y]+(1<<p)-1; 48 for(int i=15;i>=0;i--) 49 if (dep[f[x][i]]>=d)x=f[x][i]; 50 v.push_back(id(x,p)); 51 } 52 void dfs(int k,int fa,int s){ 53 dep[k]=s; 54 f[k][0]=fa; 55 for(int i=1;i<=15;i++)f[k][i]=f[f[k][i-1]][i-1]; 56 for(int i=0;i<v[k].size();i++) 57 if (v[k][i]!=fa)dfs(v[k][i],k,s+1); 58 } 59 void add(int x,int y,int z){ 60 edge[E].nex=head[x]; 61 edge[E].to=y; 62 edge[E].len=z; 63 head[x]=E++; 64 } 65 void dij(int s){ 66 memset(d,oo,sizeof(d)); 67 d[s]=0; 68 q.push(mp(0,s)); 69 while (!q.empty()){ 70 int k=q.top().second; 71 q.pop(); 72 if (vis[k])continue; 73 vis[k]=1; 74 for(int i=head[k];i!=-1;i=edge[i].nex) 75 if (d[edge[i].to]>d[k]+edge[i].len){ 76 d[edge[i].to]=d[k]+edge[i].len; 77 q.push(mp(-d[edge[i].to],edge[i].to)); 78 } 79 } 80 } 81 int main(){ 82 scanf("%d%d%d",&n,&m,&s); 83 for(int i=1;i<=n;i++)fa[i]=i; 84 for(int i=1;i<=m;i++){ 85 scanf("%d%d%d",&a[i].p,&a[i].x1,&a[i].y1); 86 if (a[i].p==1)scanf("%d%d",&a[i].x2,&a[i].y2); 87 else{ 88 if (check(a[i].x1,a[i].y1)){ 89 merge(a[i].x1,a[i].y1); 90 v[a[i].x1].push_back(a[i].y1); 91 v[a[i].y1].push_back(a[i].x1); 92 } 93 } 94 scanf("%d",&a[i].w); 95 } 96 for(int i=1;i<=n;i++) 97 if (!f[i][0])dfs(i,i,0); 98 for(int i=1;i<=n;i++)fa[i]=i; 99 memset(head,-1,sizeof(head)); 100 V=id(n,15); 101 for(int i=1;i<=n;i++){ 102 add(id(i,0),id(i,0)+V,0); 103 add(id(i,0)+V,id(i,0),0); 104 } 105 for(int i=1;i<=n;i++) 106 for(int j=1;j<=15;j++){ 107 add(id(i,j-1),id(i,j),0); 108 add(id(f[i][j-1],j-1),id(i,j),0); 109 add(id(i,j)+V,id(i,j-1)+V,0); 110 add(id(i,j)+V,id(f[i][j-1],j-1)+V,0); 111 } 112 for(int i=1;i<=m;i++){ 113 if (a[i].p==2){ 114 if (check(a[i].x1,a[i].y1)){ 115 merge(a[i].x1,a[i].y1); 116 add(id(a[i].x1,0),id(a[i].y1,0)+V,a[i].w); 117 add(id(a[i].y1,0),id(a[i].x1,0)+V,a[i].w); 118 } 119 } 120 else{ 121 if ((check(a[i].x1,a[i].y1))||(check(a[i].x2,a[i].y2)))continue; 122 vl.clear(),vr.clear(); 123 int z=lca(a[i].x1,a[i].y1); 124 div(a[i].x1,z,vl); 125 div(a[i].y1,z,vl); 126 z=lca(a[i].x2,a[i].y2); 127 div(a[i].x2,z,vr); 128 div(a[i].y2,z,vr); 129 for(int x=0;x<vl.size();x++) 130 for(int y=0;y<vr.size();y++)add(vl[x],vr[y]+V,a[i].w); 131 } 132 } 133 dij(id(s,0)); 134 for(int i=1;i<=n;i++){ 135 int ans=d[id(i,0)+V]; 136 if (ans==oo)ans=-1; 137 printf("%d ",ans); 138 } 139 }