[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$相连的最小非树边即可(最小生成树中,一个环上最大边必然为非树边)
  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 }
View Code

 

posted @ 2020-08-27 09:36  PYWBKTDA  阅读(156)  评论(0编辑  收藏  举报