• [BZOJ 2654]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

    Sample Output

    2

    Hint

    V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

    题解

    二分+$kruskal$

    如果直接$kruskal$求最小生成树,是无法保证白边数量的,那么我们考虑如果改变白边的数量。我们可以把白边全部都加上一个权值,也就是我们二分的值,然后跑最小生成树,同时记录白边数量。当白边数量>=$need$时,$l=mid+1$,否则$r=mid−1$,更新答案就是这棵生成树的权值和减去所有白边的增量。

    证明:
    我们发现,如果我们给白边增加权值,做最小生成树,由于白边权值增大,导致不容易选白边。记$f(x)$为给白边增加$x$($x$可为负)权值,做最小生成树后,选白边的数量。可以发现,$f(x)$随$x$增大而减小,显然可以二分。
    其次,我们发现,由于黑边的权值是不变的,与白边权值不相互影响。同样由于白边之间关系相对不变,必然选出的$need$条白边一定是符合题意的。

     1 #include<map>
     2 #include<ctime>
     3 #include<cmath>
     4 #include<queue>
     5 #include<stack>
     6 #include<cstdio>
     7 #include<string>
     8 #include<vector>
     9 #include<cstring>
    10 #include<cstdlib>
    11 #include<iostream>
    12 #include<algorithm>
    13 #define LL long long
    14 #define RE register
    15 #define IL inline
    16 using namespace std;
    17 const int V=50000;
    18 const int E=100000;
    19 
    20 int mid;
    21 int v,e,need,ans,cnt,tmp;
    22 struct tt
    23 {
    24     int u,v,c,col;
    25 }edge[E+5];
    26 
    27 IL int Kruskal();
    28 bool comp(const tt &a,const tt &b) {return a.c+(a.col^1)*mid<b.c+(b.col^1)*mid;}
    29 
    30 int set[V+5];
    31 IL int find(int r) {return set[r]!=-1 ? set[r]=find(set[r]):r;}
    32 
    33 int main()
    34 {
    35     scanf("%d%d%d",&v,&e,&need);
    36     for  (RE int i=1;i<=e;i++) scanf("%d%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c,&edge[i].col);
    37     int l=-100,r=100;
    38     while (l<=r)
    39     {
    40         mid=(l+r)>>1;
    41         if (Kruskal()>=need) l=mid+1,ans=tmp;
    42         else r=mid-1;
    43     }
    44     printf("%d
    ",ans);
    45     return 0;
    46 }
    47 
    48 IL int Kruskal()
    49 {
    50     tmp=cnt=0;
    51     int k=0;
    52     memset(set,-1,sizeof(set));
    53     sort(edge+1,edge+1+e,comp);
    54     for (RE int i=1;i<=e;i++)
    55     {
    56         int q=find(edge[i].u);
    57         int p=find(edge[i].v);
    58         if (p!=q)
    59         {
    60             k+=edge[i].col^1;
    61             set[q]=p;
    62             cnt++;
    63             tmp+=edge[i].c;
    64             if (cnt==v-1) break;
    65         }
    66     }
    67     return k;
    68 }
    BZOJ能过的解法

    感谢Hzoi_Maple

    由于$COGS$数据会有不满足恰好$need$条白边的情况

    打个比方有这样的数据:加$0$时大于$need$,加$1$就小于$need$了。

    这样应该在跑最小生成树的时候把所有的白边都加上加的那个权值,结果就是最小生成树的权值和减去$need*$加上的权值,多出来的那一部分完全可以当做黑边来看,因为数据是$100000$的,这样就可以了。(来自Hzoi_Maple

    排序的时候,如果边权相同,要把白边放在前面。

    要计算当前至多能取多少白边,当然要把白边放前面。由于保证有解,在$cnt>=need$且$cnt$取最小值的方案下,一定能有黑边把多余的白边代替掉。

     1 #include<map>
     2 #include<ctime>
     3 #include<cmath>
     4 #include<queue>
     5 #include<stack>
     6 #include<cstdio>
     7 #include<string>
     8 #include<vector>
     9 #include<cstring>
    10 #include<cstdlib>
    11 #include<iostream>
    12 #include<algorithm>
    13 #define LL long long
    14 #define RE register
    15 #define IL inline
    16 using namespace std;
    17 const int V=50000;
    18 const int E=100000;
    19 
    20 int mid;
    21 int v,e,need,ans,cnt,tmp;
    22 struct tt
    23 {
    24     int u,v,c,col,rc;
    25 }edge[E+5];
    26 
    27 IL int Kruskal();
    28 IL void change();
    29 bool comp(const tt &a,const tt &b) {return a.rc==b.rc ? a.col<b.col:a.rc<b.rc;}
    30 
    31 int set[V+5];
    32 IL int find(int r) {return set[r]!=-1 ? set[r]=find(set[r]):r;}
    33 
    34 int main()
    35 {
    36     scanf("%d%d%d",&v,&e,&need);
    37     for  (RE int i=1;i<=e;i++) scanf("%d%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c,&edge[i].col);
    38     int l=-100,r=100;
    39     while (l<=r)
    40     {
    41         mid=(l+r)>>1;
    42         if (Kruskal()>=need) l=mid+1,ans=tmp-need*mid;
    43         else r=mid-1;
    44     }
    45     printf("%d
    ",ans);
    46     return 0;
    47 }
    48 
    49 IL void change()
    50 {
    51     for (RE int i=1;i<=e;i++) edge[i].rc=edge[i].c+(edge[i].col^1)*mid;
    52 }
    53 IL int Kruskal()
    54 {
    55     change();
    56     tmp=cnt=0;
    57     int k=0;
    58     memset(set,-1,sizeof(set));
    59     sort(edge+1,edge+1+e,comp);
    60     for (RE int i=1;i<=e;i++)
    61     {
    62         int q=find(edge[i].u);
    63         int p=find(edge[i].v);
    64         if (p!=q)
    65         {
    66             k+=edge[i].col^1;
    67             set[q]=p;
    68             cnt++;
    69             tmp+=edge[i].rc;
    70             if (cnt==v-1) break;
    71         }
    72     }
    73     return k;
    74 }
    COGS能过的解法
    博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/NaVi-Awson/,否则你会终生找不到妹子!!!
  • 相关阅读:
    Sharepoint 2010 无法上传文件的问题
    复杂领域的Cynefin模型和Stacey模型
    SCOM Visio监控 与sharepoint 2010 整合
    HillStone上网认证客户端
    jQuery插件手把手教会(二)
    jQuery插件手把手教会(一)
    jQuery+css+div--一些细节详解
    jQuery+css+div一些值得注意的常用语句
    找不到对应的webservice配置参数[ProcessService]
    NC保存报dirty解决方法
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/7252243.html
Copyright © 2020-2023  润新知