题解:P13078 [NOISG 2019] Rigged Roads

· · 题解

P13078 [NOISG 2019] Rigged Roads

题意

给定一个 n 个点 m 条边的无向连通图,第 i 条边编号为 i,现在给定一个生成树边集 R,求一个长度为 m 的字典序最小的排列 W,使得在第 i 条边的边权为 W_i 时,最小生成树的边集恰好为 R

# 思路 考虑 kruskal 最小生成树的过程,那么一条边没有成为树边当且仅当它的两个端点在树上的路径上的任意一条边的边权都要比它小。 既然要求排列字典序最小,显然让编号更小的边在合法范围内获得更小的边权是最好的。 于是我们考虑按编号从小到大枚举边,若边 $i$ 在 $R$ 内,直接赋上当前最小的可行边权。若边 $i$ 不在 $R$ 中,为了给它赋上最小边权,显然我们要给它两个端点在树上的路径的所有边都赋上最小边权。若赋完后当前最小边权为 $x$,则给它赋上 $x+1$。 考虑如何维护这个过程。具体地,我们维护一个并查集,每一个并查集的祖先都是在树上深度最小的点。当一条边 $fa_x\to x$ 被赋了边权,则合并 $x$ 和 $x$ 的父亲 $fa_x$ 的并查集,这样在给树上路径赋边权的时候就可以直接跳并查集了。 注意,在给树上路径赋边权时,也要按照路径上还没赋边权的边的编号升序排序后赋权。 均摊复杂度 $\Omicron(n\log n)$,$\log n$ 时给树上路径赋权时给边按编号排序带的。 # 代码 ```cpp #include<bits/stdc++.h> #define ll long long using namespace std; template<typename T> inline void read(T &x){ x=0; T w=1; char c=getchar(); while(!isdigit(c)){ if(c=='-') w=-1; c=getchar(); } while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar(); x*=w; } template<typename T> inline void write(T x){ if(x<0) putchar('-'),x=(~x)+1; if(x>9) write(x/10); putchar(x%10^48); } const int N=3e5+10; int n,m; struct edge{ int u,v; }e[N]; struct data{ int v,id; }; vector<data>v[N]; int fe[N],dfn[N],ip,dep[N],lg[N],st[21][N],F[N],fa[N]; int sum[N],cnt,pos,lu[N]; bool tag[N]; void dfs(int u,int ff,int ffe){ dfn[u]=++ip; dep[u]=dep[ff]+1; st[0][dfn[u]]=u; fe[u]=ffe; fa[u]=ff; F[u]=u; for(auto p : v[u]){ if(p.v==ff) continue; dfs(p.v,u,p.id); } } int Min(int x,int y){ return dep[x]<dep[y] ? x : y; } void init(){ for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1; for(int i=1;i<=lg[n];i++){ for(int j=1;j+(1<<i)-1<=n;j++) st[i][j]=Min(st[i-1][j],st[i-1][j+(1<<i-1)]); } } int lca(int x,int y){ if(x==y) return x; x=dfn[x],y=dfn[y]; if(x>y) swap(x,y); int k=lg[y-x]; return fa[Min(st[k][x+1],st[k][y-(1<<k)+1])]; } int fin(int x){ return F[x]==x ? x : F[x]=fin(F[x]); } void mer(int u,int v){ int x=fin(u),y=fin(v); if(x==y) return ; if(dep[x]<dep[y]) F[y]=x; else F[x]=y; } void sol(int x,int y){ x=fin(x); // cout<<x<<" "<<y<<"\n"; while(dep[x]>dep[y]){ lu[++pos]=fe[x]; mer(x,fa[x]); x=fin(fa[x]); } } void solve(int u,int v){ int l=lca(u,v); pos=0; // cout<<u<<" "<<v<<" "<<l<<"\n"; sol(u,l),sol(v,l); sort(lu+1,lu+pos+1); for(int i=1;i<=pos;i++) sum[lu[i]]=++cnt; // cout<<"\n"; } int main(){ // freopen("road.in","r",stdin); // freopen("road.out","w",stdout); read(n),read(m); for(int i=1;i<=m;i++) read(e[i].u),read(e[i].v); for(int i=1;i<n;i++){ int x; read(x); tag[x]=1; v[e[x].u].push_back({e[x].v,x}); v[e[x].v].push_back({e[x].u,x}); } dfs(1,0,0); init(); for(int i=1;i<=m;i++){ if(sum[i]) continue; else{ if(tag[i]) sum[i]=++cnt,mer(e[i].u,e[i].v); else solve(e[i].u,e[i].v),sum[i]=++cnt; } } for(int i=1;i<=m;i++) write(sum[i]),putchar(' '); return 0; } ```