cogs1764 tree 最小生成树

填坑第$n$日……链接:http://cogs.pro/cogs/problem/problem.php?pid=1764

题意:图中有黑白双色边,要求构造出的生成树必须有恰好$need$条白色边,求出在此条件下最小生成树权值和。保证有解

为了这道题我冒着查到被杀的风险在学校不午休逃到机房……敲了一中午再加上下午第一节课……做出来之后只能无限$orzWJMZBMR$……

首先可以证明出来,如果给白色边权值全部加上一个定值,新生成的最小生成树中白色边的数量是不增的。我们就利用这个性质,手动给白色边全部加上一个权值,再建树,看看在这个权值下是不是可以抽出$need$条边。由于存在单调性,显然可以二分答案(加粗的字已经说明了一切)。如果这个值合法,那么就将这个情况下答案对应的白边增加的权值再减回去更新答案。

但是这样会带来一个问题:有一些测试点,你加上$mid$数量太少,你加上$mid+1$数量又太多。这种情况下我们就要考虑$Kruskal$的原理,然后我们会发现,这种情况出现前提是:白边与黑边权值重复太多。解决办法很简单也很巧妙:把白边用黑边代替,更新答案时只减去$need$条边造成的影响即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 const int maxn=50005,maxm=100005;
 7 struct node
 8 {
 9     int from,to,dis,col,next;
10     bool operator <(const node &b)const
11     {
12         return dis<b.dis||(dis==b.dis&&col<b.col);
13     }
14 }edge[maxm];
15 int head[maxn],tot;
16 void addedge(int u,int v,int w,int x)
17 {
18     edge[++tot]=(node){u,v,w,x,head[u]};head[u]=tot;
19 }
20 int fa[maxn];
21 int getfa(int x)
22 {
23     return fa[x]==x?x:fa[x]=getfa(fa[x]);
24 }
25 void unionn(int x,int y)
26 {
27     x=getfa(x),y=getfa(y);
28     if(x!=y)fa[y]=x;
29 }
30 int n,m,need;
31 int val;
32 bool check(int add)
33 {
34     val=0;
35     for(int i=0;i<n;i++)fa[i]=i;
36     for(int i=1;i<=m;i++)
37         if(!edge[i].col)edge[i].dis+=add;
38     sort(edge+1,edge+m+1);int num=0,cnt=0;
39     for(int i=1;i<=m;i++)
40     {
41         int u=edge[i].from,v=edge[i].to;
42         if(getfa(u)!=getfa(v))
43         {
44             unionn(u,v);
45             val+=edge[i].dis;
46             if(!edge[i].col)cnt++;
47         }
48     }
49     for(int i=1;i<=m;i++)
50         if(!edge[i].col)edge[i].dis-=add;
51     return cnt>=need;
52 }
53 int haha()
54 {
55     freopen("nt2012_tree.in","r",stdin);
56     freopen("nt2012_tree.out","w",stdout);
57     scanf("%d%d%d",&n,&m,&need);
58     for(int i=1;i<=m;i++)
59     {
60         int u,v,w,x;scanf("%d%d%d%d",&u,&v,&w,&x);
61         addedge(u,v,w,x);
62     }
63     int l=-105,r=105,mid,query;
64     while(l<=r)
65     {
66         mid=(l+r)>>1;
67         if(check(mid))l=mid+1,query=val-mid*need;
68         else r=mid-1;
69     }
70     printf("%d\n",query);
71 }
72 int sb=haha();
73 int main(){;}
cogs1764

 

posted @ 2017-08-13 20:43  ccc000111  阅读(145)  评论(0编辑  收藏  举报