[loj3346]交换城市
观察可得,$(x,y)$能相互到达当且仅当:1.$x$和$y$联通;2.$x$和$y$所在的连通块不为链
根据这个结论,可以二分枚举答案+暴力判定,复杂度$o(qm\log_{2}1e9)$,可以通过$Subtask\ 1-4$
考虑$Subtask\ 5$,即构造出一棵联通子图使得:包含$x$到$y$的链且存在度超过2的点,之后最小化边权最大值
设一个度超过2的点为$z$,贪心可以发现存在最优解为$x,y,z$联通的最小子图+与$z$相邻的3条最小的边(已经出现了就不用添加,这保证了答案合法且答案必然不小于第3小的边权)
边权是取$\max$,重复计算不影响结果,因此边权最大值为$\max(mx(x,y),mx(x,z),mn[z])$(其中$mx(x,y)$表示$x$到$y$的链上最大边,$mn[z]$表示与$z$相邻的第3小边的边权),即最小化这个式子
提出$mx(x,y)$,对后者预处理:先dp求出子树内的最小值和次小值,子树外的最小值即min(父亲,父亲子树外最小值,兄弟子树内最小值),其中兄弟子树内最小值分类讨论即可,最终复杂度为$o((q+n)\log_{2}n)$($\log$为倍增计算$mx(x,y)$)
考虑一张图,与树不同的地方有两点:
1.由于两点间路径不唯一,$mx(x,y)$的定义需要改为最大边权最小的路径
2.并不一定存在度超过2的点,但如果不存在必然整个连通块为一个环
先求出最小生成树,对于第1点,可以证明这条路径一定是最小生成树上的路径,同样被倍增计算即可
考虑第2点,与找到度大于2的点类似,必然存在一个点与非树边相连,同样记为$z$,那么只需要将$mn[z]$改为与$z$相连的最小非树边即可(最小生成树中,一个环上最大边必然为非树边)
View Code
1 #include "swap.h" 2 #include<bits/stdc++.h> 3 using namespace std; 4 #define N 100005 5 struct ji{ 6 int nex,to,len; 7 }edge[N<<1]; 8 pair<int,int>id[N<<1]; 9 int E,n,m,head[N],r[N],mn[N],sh[N],f[N],ff[N],fo[N],fa[N][21],mx[N][21]; 10 int find(int k){ 11 if (k==f[k])return k; 12 return f[k]=find(f[k]); 13 } 14 void add(int x,int y,int z){ 15 edge[E].nex=head[x]; 16 edge[E].to=y; 17 edge[E].len=z; 18 head[x]=E++; 19 } 20 int lca(int x,int y){ 21 if (sh[x]<sh[y])swap(x,y); 22 for(int i=20;i>=0;i--) 23 if (sh[fa[x][i]]>=sh[y])x=fa[x][i]; 24 if (x==y)return x; 25 for(int i=20;i>=0;i--) 26 if (fa[x][i]!=fa[y][i]){ 27 x=fa[x][i]; 28 y=fa[y][i]; 29 } 30 return fa[x][0]; 31 } 32 int calc(int x,int y){ 33 int ans=0; 34 for(int i=20;i>=0;i--) 35 if (sh[fa[x][i]]>=sh[y]){ 36 ans=max(ans,mx[x][i]); 37 x=fa[x][i]; 38 } 39 return ans; 40 } 41 void dfs(int k,int f,int s){ 42 sh[k]=s; 43 fa[k][0]=f; 44 for(int i=1;i<=20;i++){ 45 fa[k][i]=fa[fa[k][i-1]][i-1]; 46 mx[k][i]=max(mx[k][i-1],mx[fa[k][i-1]][i-1]); 47 } 48 for(int i=head[k];i!=-1;i=edge[i].nex) 49 if (edge[i].to!=f){ 50 mx[edge[i].to][0]=edge[i].len; 51 dfs(edge[i].to,k,s+1); 52 } 53 } 54 void dp1(int k,int fa){ 55 f[k]=mn[k]; 56 ff[k]=0x3f3f3f3f; 57 for(int i=head[k];i!=-1;i=edge[i].nex) 58 if (edge[i].to!=fa){ 59 dp1(edge[i].to,k); 60 int x=max(f[edge[i].to],edge[i].len); 61 if (f[k]>x)swap(f[k],x); 62 if (ff[k]>x)swap(ff[k],x); 63 } 64 } 65 void dp2(int k,int fa){ 66 if (f[fa]!=max(f[k],mx[k][0]))fo[k]=min(fo[fa],f[fa]); 67 else fo[k]=min(fo[fa],ff[fa]); 68 if (k==0)fo[k]=0x3f3f3f3f; 69 fo[k]=max(fo[k],mx[k][0]); 70 for(int i=head[k];i!=-1;i=edge[i].nex) 71 if (edge[i].to!=fa)dp2(edge[i].to,k); 72 } 73 void init(int nn,int mm,vector<int>u,vector<int>v,vector<int>w){ 74 n=nn; 75 m=mm; 76 for(int i=0;i<m;i++)id[i]=make_pair(w[i],i); 77 sort(id,id+m); 78 E=0; 79 memset(head,-1,sizeof(head)); 80 memset(mn,0x3f,sizeof(mn)); 81 for(int i=0;i<n;i++)f[i]=i; 82 for(int i=0;i<m;i++){ 83 int k=id[i].second,x=find(u[k]),y=find(v[k]); 84 if ((x==y)||(++r[u[k]]==3))mn[u[k]]=min(mn[u[k]],w[k]); 85 if ((x==y)||(++r[v[k]]==3))mn[v[k]]=min(mn[v[k]],w[k]); 86 if (x!=y){ 87 f[x]=y; 88 add(u[k],v[k],w[k]); 89 add(v[k],u[k],w[k]); 90 } 91 } 92 dfs(0,0,0); 93 dp1(0,0); 94 dp2(0,0); 95 } 96 int getMinimumFuelCapacity(int x,int y){ 97 int z=lca(x,y),ans=max(max(calc(x,z),calc(y,z)),min(f[x],fo[x])); 98 if (ans==0x3f3f3f3f)ans=-1; 99 return ans; 100 }