首页 > 代码库 > BZOJ1977: [BeiJing2010组队]次小生成树 Tree
BZOJ1977: [BeiJing2010组队]次小生成树 Tree
1977: [BeiJing2010组队]次小生成树 Tree
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 2108 Solved: 463
[Submit][Status]
Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
HINT
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
Source
题解:
真是不能再sb了。。。数组越界+xy打反。。。
如果不是严格的只需要枚举每条边,在MST上找这两点之间最大的边记作x,与当前枚举边的权值记作y,用y-x更新答案即可。
但是如果是严格的话?就会出现y==x的情况,那我们可以不管这条边吗?
当然不行,次小生成树是由MST加上一条边删去另一条边得到的。
想想看如果去掉此时两点间的次大值,加上y,也有可能成为答案。第三大就不会了。
所以我们需要找次大边,分情况来更新答案。
我还有点不懂的地方就是:
不同的两个MST最多有多少条边不同?怎么保证任何一个MST通过本题的操作都能的得到正确的答案?
求神犇指教。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 100000+100 14 #define maxm 300000+100 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 using namespace std; 22 inline int read() 23 { 24 int x=0,f=1;char ch=getchar(); 25 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} 26 while(ch>=‘0‘&&ch<=‘9‘){x=10*x+ch-‘0‘;ch=getchar();} 27 return x*f; 28 } 29 int dep[maxn],fa[maxn],head[maxn],tot,n,m,f[maxn][22]; 30 bool out[maxm]; 31 struct recc{int a,b;} g[maxn][22],ret; 32 struct edge{int go,next,w;}e[2*maxn]; 33 struct rec{int x,y,z;}a[maxm]; 34 inline void insert(int x,int y,int w) 35 { 36 e[++tot].go=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot; 37 e[++tot].go=x;e[tot].w=w;e[tot].next=head[y];head[y]=tot; 38 } 39 inline bool cmp(rec a,rec b) 40 { 41 return a.z<b.z; 42 } 43 inline void update(recc &x,recc &y) 44 { 45 if(y.a<x.a)y.b=y.a,y.a=x.a; 46 else {if(x.a>y.b&&x.a<y.a)y.b=x.a;} 47 if(x.b==-1)return; 48 if(y.a<x.b)y.b=y.a,y.a=x.b; 49 else {if(x.b>y.b&&x.b<y.a)y.b=x.b;} 50 } 51 inline void dfs(int x) 52 { 53 for1(i,20) 54 if((1<<i)<=dep[x]) 55 { 56 f[x][i]=f[f[x][i-1]][i-1]; 57 g[x][i].a=g[x][i].b=-1; 58 update(g[f[x][i-1]][i-1],g[x][i]); 59 update(g[x][i-1],g[x][i]); 60 } 61 else break; 62 for(int i=head[x],y;i;i=e[i].next) 63 if(!dep[y=e[i].go]) 64 { 65 f[y][0]=x;g[y][0].a=e[i].w;g[y][0].b=-1; 66 dep[y]=dep[x]+1; 67 dfs(y); 68 } 69 } 70 inline void query(int x,int y) 71 { 72 if(dep[x]<dep[y])swap(x,y); 73 int t=dep[x]-dep[y]; 74 ret.a=ret.b=-1; 75 for0(i,20) 76 if(t&(1<<i)) 77 { 78 update(g[x][i],ret); 79 x=f[x][i]; 80 } 81 if(x==y)return; 82 for(int i=20;i>=0;i--) 83 if(f[x][i]!=f[y][i]) 84 { 85 86 update(g[x][i],ret);x=f[x][i]; 87 update(g[y][i],ret);y=f[y][i]; 88 } 89 update(g[x][0],ret);update(g[y][0],ret); 90 } 91 inline int find(int x) 92 { 93 return fa[x]==x?x:fa[x]=find(fa[x]); 94 } 95 int main() 96 { 97 freopen("input.txt","r",stdin); 98 freopen("output.txt","w",stdout); 99 n=read();m=read();100 for1(i,m)a[i].x=read(),a[i].y=read(),a[i].z=read();101 sort(a+1,a+m+1,cmp);102 for1(i,n)fa[i]=i;103 ll sum=0,ans=inf;104 for(int i=1,j=1;i<=n-1;i++)105 {106 while(find(a[j].x)==find(a[j].y))j++;107 fa[find(a[j].x)]=find(a[j].y);insert(a[j].x,a[j].y,a[j].z);108 out[j]=1;109 sum+=a[j].z;j++;110 }111 dep[1]=1;g[1][0].a=g[1][0].b=-1;112 dfs(1);113 for1(i,m)if(!out[i])114 {115 int x=a[i].x,y=a[i].y,z=a[i].z;query(x,y);116 if(ret.a!=z&&z-ret.a<ans)ans=z-ret.a;117 if(ret.a==z&&ret.b!=-1&&z-ret.b<ans)ans=z-ret.b;118 }119 printf("%lld\n",ans+sum);120 return 0;121 }
BZOJ1977: [BeiJing2010组队]次小生成树 Tree