BZOJ 2654 tree
2654: tree
Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
Sample Input
2 2 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
幸好保证有解,不然本人是很懵的。这道题目很有趣,值得去想一想。
这道题,要求恰好取need条白色边,然后做最小生成树。平常,最小生成树是相对一定的。怎么办呢?
注意到白边的优先级可以变化(排序时加减“虚”边权,统计时不加入),且存在着优先级与选取边数的单调性。我们让白边公平竞争,集体“虚”边权加减,二分其值([-105,105]),每一次再kruscal,看选取边数。最后输出结果。
所以说,复杂度是二分O(lg n)*MST.O(nlg n)。
1 /************************************************************** 2 Problem: 2654 3 User: Doggu 4 Language: C++ 5 Result: Accepted 6 Time:1520 ms 7 Memory:3164 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <algorithm> 12 template<class T>inline void readin(T &res) { 13 static char ch;T flag=1;while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1; 14 res=ch-48;while((ch=getchar())>='0'&&ch<='9')res=(res<<1)+(res<<3)+ch-48;res*=flag; 15 } 16 const int N = 100000+10; 17 const int M = 100000+10; 18 struct Edge {int u, v, w, col, add;}edges[M]; 19 bool operator<(Edge a,Edge b) {return a.w+a.add<b.w+b.add;} 20 int n, m, need, fa[N]; 21 int find(int x) { 22 if(fa[x]==x) return x; 23 return fa[x]=find(fa[x]); 24 } 25 int main() { 26 readin(n);readin(m);readin(need); 27 for( int i = 1; i <= m; i++ ) { 28 readin(edges[i].u);readin(edges[i].v); 29 readin(edges[i].w);readin(edges[i].col); 30 } 31 int lf=-105, rg=105, ans=0; 32 while(lf<=rg) { 33 int mid=(lf+rg)>>1, k=0, tot=0; 34 for( int i = 1; i <= m; i++ ) if(!edges[i].col) edges[i].add=mid; 35 for( int i = 0; i < n; i++ ) fa[i]=i; 36 std::sort(edges+1,edges+m+1); 37 for( int i = 1; i <= m; i++ ) { 38 if(find(edges[i].u)!=find(edges[i].v)) { 39 if(!edges[i].col) k++; 40 tot+=edges[i].w; 41 fa[fa[edges[i].u]]=edges[i].v; 42 } 43 } 44 if(k>=need) ans=tot, lf=mid+1; 45 else rg=mid-1; 46 } 47 printf("%d\n",ans); 48 return 0; 49 }