题目描述
给定一个标号为从 11 到 nn 的、有 mm 条边的无向图,求边权最大值与最小值的差值最小的生成树。
输入输出格式
输入格式:
第一行两个数 n, mn,m ,表示图的点和边的数量。
第二行起 mm 行,每行形如 u_i, v_i, w_iui,vi,wi ,代表 u_iui 到 v_ivi 间有一条长为 w_iwi 的无向边。
输出格式:
输出一行一个整数,代表你的答案。
数据保证存在至少一棵生成树。
输入输出样例
说明
对于 30% 的数据,满足 1 leq n leq 100, 1 leq m leq 10001≤n≤100,1≤m≤1000
对于 97% 的数据,满足 1 leq n leq 500, 1 leq m leq 1000001≤n≤500,1≤m≤100000
对于 100% 的数据,满足 1 leq n leq 50000, 1 leq m leq 200000, 1 leq w_i leq 100001≤n≤50000,1≤m≤200000,1≤wi≤10000
题解:有自环是真的毒瘤……反正思路是从大到小枚举每条边作为最小边构成的生成树,然后显然他要加进去就要把原环上最大值扔掉,这样子动态加边删边,就可以搞出答案了
还有个问题是怎么维护最小生成树里的最大值,因为权值很小,所以可以考虑搞一个cnt,一开始pos等于最大边w,接着如果最大边被删掉了,就暴力往下跳pos,直到cnt[pos]>0为止,这样子均摊一下复杂度是O(1)的
代码如下:
#include<map> #include<set> #include<queue> #include<cmath> #include<cstdio> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define N 300010 #define M 350010 #define lson ch[x][0] #define rson ch[x][1] using namespace std; int cnt[10010],max1,n,m,ans=0x3f3f3f3f; struct node { int from,to,w; } e[N]; int cmp(node a,node b) { return a.w<b.w; } //lct begin int tag[M],f[M],ch[M][2],sum[M]; int not_root(int now) { int x=f[now]; return lson==now||rson==now; } int push_up(int x) { sum[x]=x; if(e[sum[lson]].w>e[sum[x]].w) { sum[x]=sum[lson]; } if(e[sum[rson]].w>e[sum[x]].w) { sum[x]=sum[rson]; } } int rev(int x) { swap(lson,rson); tag[x]^=1; } int push_down(int x) { if(tag[x]) { rev(lson); rev(rson); tag[x]^=1; } } int rotate(int x) { int y=f[x],z=f[y],kd=ch[y][1]==x,xs=ch[x][!kd]; if(not_root(y)) { ch[z][ch[z][1]==y]=x; } ch[x][!kd]=y; ch[y][kd]=xs; if(xs) f[xs]=y; f[y]=x; f[x]=z; push_up(y); } int push_all(int x) { if(not_root(x)) { push_all(f[x]); } push_down(x); } int splay(int x) { int y,z; push_all(x); while(not_root(x)) { y=f[x],z=f[y]; if(not_root(y)) { (ch[y][1]==x)^(ch[z][1]==y)?rotate(x):rotate(y); } rotate(x); } push_up(x); } int access(int x) { for(int y=0; x; y=x,x=f[x]) { splay(x); rson=y; push_up(x); } } int make_root(int x) { access(x); splay(x); rev(x); } int split(int x,int y) { make_root(x); access(y); splay(y); } int find_root(int x) { access(x); splay(x); while(lson) { push_down(x); x=lson; } return x; } int link(int x,int y) { make_root(x); if(find_root(y)==x) return 0; f[x]=y; return 1; } int cut(int x,int y) { make_root(x); if(find_root(y)!=x||f[x]!=y||rson) return 0; f[x]=ch[y][0]=0; push_up(y); return 1; } int print(int x) { if(lson) print(lson); printf("%d ",x); if(rson) print(rson); } //lct end //dsu begin int fa[M]; int init() { for(int i=1; i<M; i++) { fa[i]=i; } } int find(int x) { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } int unity(int x,int y) { int fx=find(x); int fy=find(y); if(fx==fy) return 0; fa[fx]=fy; return 1; } //dsu end int main() { init(); scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) { scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].w); } sort(e+1,e+m+1,cmp); int tot=0; for(int i=m; i>=1; i--) { if(e[i].from==e[i].to) continue; if(unity(e[i].from,e[i].to)) { tot++; link(e[i].from+m,i); link(e[i].to+m,i); cnt[e[i].w]++; max1=max(max1,e[i].w); if(tot==n-1) ans=max1-e[i].w; } else { split(e[i].from+m,e[i].to+m); int gg=sum[e[i].to+m]; cut(e[gg].to+m,gg); cut(e[gg].from+m,gg); cnt[e[gg].w]--; cnt[e[i].w]++; while(!cnt[max1]) { max1--; } if(tot==n-1) ans=min(ans,max1-e[i].w); link(e[i].to+m,i); link(e[i].from+m,i); } } printf("%d ",ans); }