暑假考试题5:tree 最小生成树(最小生成树+倍增)
作者:互联网
题目:
分析:
转换问题:先求出一颗最小生成树
对于非树边来说,连上非树边一定会成环,而可以使非树边成为树边的即是环上max-1
对于树边来说,它不能无限增大,是由于受到非树边的影响,而答案即为能影响它的非树边的min-1
第一个只需要求链最大值,用倍增解决
第二个:对于每一条非树边,都要更新一条链,但是可以不用写链剖
做法:将非树边从小到大排好序,显然一条边先被小的更新了,就不会被大的更新了,所以开一个ff数组
表示一个点到深度比它浅的点那条路径(不经过根)已经被更新了 当枚举非树边去更新的时候 把已经被更新的跳过
保证了每条边只被更新过一次
/* 先求出一颗最小生成树 对于非树边来说,连上非树边一定会成环,而可以使非树边成为树边的即是环上max-1 对于树边来说,它不能无限增大,是由于受到非树边的影响,而答案即为能影响它的非树边的min-1 第一个只需要求链最大值,用倍增解决 第二个:对于每一条非树边,都要更新一条链,但是可以不用写链剖 做法:将非树边从小到大排好序,显然一条边先被小的更新了,就不会被大的更新了,所以开一个ff数组 表示一个点到深度比它浅的点那条路径(不经过根)已经被更新了 当枚举非树边去更新的时候 把已经被更新的跳过 保证了每条边只被更新过一次 */ #include<bits/stdc++.h> using namespace std; #define inf 1000000001 #define N 100005 int head[N],w[N<<1],to[N<<1],nex[N<<1],tot=0,fa[N],ans[N],id[N<<1]; int f[N][30],mx[N][30],dep[N],num[N],s[N],ff[N],fl[N],n,m; struct node{ int u,v,w,o,ans; }e[N]; bool cmp1(const node &a,const node &b) { return a.w<b.w; } bool cmp2(const node &a,const node &b) { return a.o<b.o; } void add(int a,int b,int ww,int i) { to[++tot]=b; nex[tot]=head[a]; w[tot]=ww; id[tot]=i; head[a]=tot; } int find(int x) { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } void dfs(int u) { for(int i=head[u];i;i=nex[i]){ int v=to[i]; if(v==f[u][0]) continue; f[v][0]=u; dep[v]=dep[u]+1; mx[v][0]=w[i]; num[v]=id[i];//id记录这个点向上连的边对应排好序后的编号 for(int j=1;j<=20;j++) f[v][j]=f[f[v][j-1]][j-1]; for(int j=1;j<=20;j++) mx[v][j]=max(mx[f[v][j-1]][j-1],mx[v][j-1]); dfs(v); } } int lca(int a,int b) { if(dep[a]>dep[b]) swap(a,b); for(int i=20;i>=0;i--) if(dep[f[b][i]]>=dep[a]) b=f[b][i];//=!! if(a==b) return a; for(int i=20;i>=0;i--) if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i]; return f[a][0]; } int lca_max(int a,int b,int lc) { int anss=-1; if(dep[a]>dep[b]) swap(a,b); for(int i=20;i>=0;i--) if(dep[f[b][i]]>=dep[a]) anss=max(anss,mx[b][i]),b=f[b][i];//先更新再跳 一定要有dep>=!! if(a==b) return anss; for(int i=20;i>=0;i--) if(f[a][i]!=f[b][i]) anss=max(anss,max(mx[a][i],mx[b][i])),a=f[a][i],b=f[b][i]; anss=max(anss,max(mx[a][0],mx[b][0])); return anss; } void work(int u,int lc,int ww) { while(dep[u]>dep[lc]){ if(!s[num[u]] || s[num[u]]>ww) s[num[u]]=ww;//更新最小值 ff[u]=lc; u=f[u][0];//记录u已经更新到哪一个点了 u一定要=fa[u] 否则会在原地不动 while(ff[u]!=u) u=ff[u]; } } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); int a,b,ww; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d",&a,&b,&ww); e[i].u=a,e[i].v=b,e[i].w=ww,e[i].o=i; } sort(e+1,e+1+m,cmp1); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++){ int f1=find(e[i].u),f2=find(e[i].v); if(f1==f2) continue; fa[f1]=f2; fl[i]=1; add(e[i].u,e[i].v,e[i].w,i); add(e[i].v,e[i].u,e[i].w,i); } dep[1]=1; dfs(1); for(int i=1;i<=n;i++) ff[i]=i;//ff数组记录每个点更新到哪一个点了 初始时为它自己 for(int i=1;i<=m;i++){ if(fl[i]) continue; int u=e[i].u,v=e[i].v,ww=e[i].w; int lc=lca(u,v),maxx=lca_max(u,v,lc); ans[i]=maxx-1;//ans存储非树边的值 while(ff[u]!=u) u=ff[u];//直接跳到已经更新到的位置 while(ff[v]!=v) v=ff[v]; work(u,lc,ww);//处理将u到v的一条链更新成最小的非树边的值 work(v,lc,ww); } for(int i=1;i<=m;i++){ if(!fl[i]) e[i].ans=ans[i]; else e[i].ans=s[i]-1; } sort(e+1,e+1+m,cmp2); for(int i=1;i<=m;i++) printf("%d\n",e[i].ans);/**/ } /* 4 4 2 1 2 3 2 2 4 3 2 1 4 3 7 8 1 2 2 1 4 3 2 3 6 2 5 8 2 7 5 1 7 9 3 6 7 2 4 4 */
标签:int,max,anss,tree,最小,更新,生成,dep,非树边 来源: https://www.cnblogs.com/mowanying/p/11420174.html