[国家集训队2012]tree(陈立杰) 题解(二分+最小生成树)

 tree

时间限制: 3 Sec  内存限制: 512 MB

题目描述

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。

输入

第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

输出

一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

样例输入

2 2 1
0 1 1 1
0 1 2 0

样例输出

2
  这道题正解是二分+最小生成树。本题最烦人的就是限制白边的数量因此我们需要一个东西使白边被优先或滞后考虑。由于克鲁斯卡尔是按照边权排序,我们可以给白边附上一个假边权,这样就可以使白边被优先或滞后考虑了。
  由于我们还需要保证改变后所有白边的相对大小不变,因此我们不能直接对白边附上某个值而是统一加上或减去某一个值,这样就可以保证相对大小不变了。至于这个值到底应当是多少,我们完全可以二分去查找,毕竟它是满足单调的。
  这时一种比较坑爹的情况就会出现了,如果这个图当所有白边边权加x后在最小生成树中的数目比need大,加x+1后数量又比need小,我们该如何处理呢?我们可以想一下,是什么样的黑边被替换了呢?就是白边新增的边的边权-附加的权值,因此如果现在算进最小生成树中的白边数大于need,其实它也是可以被算进结果的。
  
  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<map>
  7 #include<queue>
  8 #include<string>
  9 #include<cmath>
 10 using namespace std;
 11 int n,m,ne;
 12 struct ro{
 13     int from,to;
 14     int l,col,rl;
 15 }road[100005];
 16 void build(int x,int y,int bh,int z,int zx){
 17     road[bh].from=x;
 18     road[bh].to=y;
 19     road[bh].rl=road[bh].l=z;
 20     road[bh].col=zx;
 21 }
 22 bool pd=1;
 23 int px(ro a,ro b){
 24     if(a.l==b.l)
 25         return a.col<b.col;
 26     return a.l<b.l;
 27 }
 28 int fa[50005];
 29 map<int,bool> fw2[50005][102];
 30 map<int,int> fw[50005][102];
 31 void init(int x){
 32     for(int i=1;i<=n;i++)
 33         fa[i]=i;
 34     for(int i=1;i<=m;i++)
 35     {
 36         if(!road[i].col)
 37         {
 38             road[i].l=road[i].rl+x;
 39         }
 40     }
 41     sort(road+1,road+m+1,px);
 42 }
 43 int find(int x){
 44     if(fa[x]==x)
 45         return x;
 46     else
 47         return fa[x]=find(fa[x]);
 48 }
 49 void hb(int x,int y){
 50     int a=find(x);
 51     int b=find(y);
 52     if(a>b) fa[a]=b;
 53     else fa[b]=a;
 54 }
 55 int sum=0x7fffffff;
 56 bool check(int x){
 57     int js=0,ans=0,js2=0,js3=0;
 58      
 59     for(int i=1;i<=m;i++)
 60     {
 61         if(js==n-1)break;
 62         if(find(road[i].from)!=find(road[i].to))
 63         {
 64             js++;
 65             if(road[i].col==0)
 66             {
 67                 js2++;
 68             }
 69             ans+=road[i].l;
 70             hb(road[i].from,road[i].to);
 71         }
 72     }
 73     if(js2>=ne)
 74     {
 75         sum=ans-ne*x;
 76         return 1;
 77     }
 78     pd=0;
 79     return 0;
 80 }
 81 int main(){
 82     scanf("%d%d%d",&n,&m,&ne);
 83     for(int i=1;i<=m;i++)
 84     {
 85         int x,y,z,zx;
 86         scanf("%d%d%d%d",&x,&y,&z,&zx);
 87         build(x,y,i,z,zx);
 88     }
 89     int li=-100,ri=100;
 90     while(li<=ri)
 91     {
 92         int mid=(li+ri)/2;
 93         init(mid);
 94         if(check(mid))
 95         {
 96             li=mid+1;
 97         }
 98         else
 99             ri=mid-1;
100     }
101     printf("%d
",sum);
102     //while(1);
103     return 0;
104 }
View Code
 
原文地址:https://www.cnblogs.com/liutianrui/p/7360166.html