【bzoj2654】【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

数据规模和约定

  0:V<=10

  1,2,3:V<=15

  0,..,19:V<=50000,E<=100000

  所有数据边权为[1,100]中的正整数。

题解:显然可以发现随着白边权值的增大。最小生成树中白边的个数不增。

然后根据这个性质我们就可以二分一个值,然后每次给白边加上这个值。看一下最小生成树中白边的个数。

最后答案再把它减去。

看起来思路非常简单,但是有一个很重要的细节。

如果在你的二分过程中如果给白边加上mid,你得到的白边数比need大。

给白边加上mid+1,你得到的白边比need小。

这种情况看似没法处理。

但是考虑一下克鲁斯卡尔的加边顺序。

可以发现如果出现这种情况,一定是有很多相等的白边和黑边。因为数据保证合法。

所以我们可以把一些白边替换成黑边。

所以我们要在白边数>=need的时候跟新答案。

具体用ans=ans-mid*need;即可。

代码:

#include
#include
#include
using namespace std;
int fa[1000001],n,m,need,l(-105),r(105),mid,temp,ans,r1,r2,cnt,ans2;
struct use{int st,en,c,v;}e[100010];
int find(int x){if (x!=fa[x]) fa[x]=find(fa[x]);return fa[x];}
bool cmp(use a,use b){if (a.v==b.v) return a.c>1;
    for (int i=1;i<=m;i++) {if (e[i].c==0) e[i].v+=mid;}
    for (int i=1;i<=n+1;i++) fa[i]=i;ans=0;temp=0;cnt=0;
    solve();
    if (temp>=need) {l=mid+1;ans2=ans-need*mid;}else r=mid-1;
    for (int i=1;i<=m;i++) if (e[i].c==0) e[i].v-=mid;
  }
  printf("%d\n",ans2);
}


你可能感兴趣的:(生成树,二分)