BZOJ2654/COGS1764 [2012国家集训队]tree(陈立杰) [生成树,二分]
tree
Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
Sample Input
2 2 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
Hint
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
分析:
在大鸡哥的博客里看到的,一开始的分析就是直接一波玄学排序然后暴力Kruskal+一堆判断,然后自己把自己推翻了。。。
还是看了大鸡哥的博客才懂得。大鸡哥的博客里讲的很清晰了,而且还讲到了COGS上和BZOJ上数据不同的问题。就推荐一下大鸡哥的博客吧。
Code:
//It is made by HolseLee on 1st June 2018 //BZOJ 2654/COGS 1764 #include<bits/stdc++.h> using namespace std; const int N=5e4+7; const int M=1e5+7; int n,m,k,fa[N],num,ans; struct Node{ int from,to,val,c,id; }edge[M]; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } inline bool cmp(Node a,Node b) {return a.c==b.c?a.id<b.id:a.c<b.c;} inline int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);} inline int check(int ka) { for(int i=1;i<=m;i++) edge[i].c=edge[i].val+(edge[i].id^1)*ka; sort(edge+1,edge+m+1,cmp); for(int i=0;i<n;i++)fa[i]=i; int cnt=0,tot=0;num=0; for(int i=1;i<=m;i++){ int x=find(edge[i].from); int y=find(edge[i].to); if(x!=y){fa[y]=x;tot++; cnt+=(edge[i].id==0?1:0); num+=edge[i].c;} if(tot==n-1)break;} return cnt; } int main() { freopen("nt2012_tree.in","r",stdin); freopen("nt2012_tree.out","w",stdout); scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++){ scanf("%d%d",&edge[i].from,&edge[i].to); scanf("%d%d",&edge[i].val,&edge[i].id);} int L=-100,R=100,mid; while(L<=R){ mid=(L+R)/2; if(check(mid)>=k)ans=num-k*mid,L=mid+1; else R=mid-1;} printf("%d",ans);return 0; }
蒟蒻写博客不易,如果有误还请大佬们提出
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com