[国家集训队2012]tree(陈立杰)

来源:互联网 发布:js模拟按键 编辑:程序博客网 时间:2024/05/22 03:49

1764. [国家集训队2012]tree(陈立杰)

★★★   输入文件:nt2012_tree.in   输出文件:nt2012_tree.out   简单对比
时间限制:3 s   内存限制:1024 MB
tree(陈立杰)
时间限制:3.0s   内存限制:1.0GB

【大意】

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

【输入格式】

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

【输出格式】

一行表示所求生成树的边权和。

【数据规模和约定】

0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。

【样例输入】

2 2 1
0 1 1 1
0 1 2 0

【样例输出】

2



二分+kruskalkruskal

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

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


其实是傻逼题 
考虑二分一个值,给白边边权加上这个值,然后不论颜色MST,记录白边数量 
一定有一个值满足MST中白边数量恰好为k 
二分姿势不对一直WA(╯‵□′)╯︵┻━┻


可以发现如果分别给白边加-101~101,形成的“合法白边数”区间加起来一定不会漏,因为给白边加x的最少白边数,和给白边加x+1的最多白边数其实是一个意思(二者在Kruskal时加边顺序相同)


#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<set>
#include<map>
#include<deque>
#include<cstdlib>
#include<algorithm>
#define V 505000
#define mod 1000000007 
#define LL long long
using namespace std;
int f[V];
struct sdd
{
int x,y,dis,xx;
friend bool operator < (sdd x,sdd y)
{
if(x.dis==y.dis)return x.xx<y.xx;
return x.dis<y.dis;
}
}ww[V];
int head[V],tot,m,n,md,k,sb,dd,ans;
inline int find(int x)
{
if(f[x]!=x)f[x]=find(f[x]);
return f[x];
}
inline void sbd()
{
int k=0;
int xx,yy;
md=0;sb=0;
sort(ww+1,ww+m+1);
  for(int i=1;i<=n;i++)
         f[i]=i;
  for(int i=1;i<=m;i++)
       {
         xx=find(ww[i].x);
         yy=find(ww[i].y);
         if(xx!=yy)
         {
         f[yy]=xx;
sb+=ww[i].dis;
md+=!(ww[i].xx);
k++;
 }
 
if(k==n-1)break; 
  }
}
int main()
{
  //freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);
 freopen("nt2012_tree.in","r",stdin);freopen("nt2012_tree.out","w",stdout);
  memset(head,-1,sizeof(head));
  int xx,yy;
  cin>>n>>m>>dd;
  int sz=m;
  while(m--)
  {
   cin>>ww[m+1].x>>ww[m+1].y>>ww[m+1].dis>>ww[m+1].xx; 
    ww[m+1].x++;ww[m+1].y++;
  }
  m=sz;
       int l=-101,r=101,mid;
  while(l<=r)
       {
        mid=(l+r)/2;
        for(int i=1;i<=m;i++) ww[i].dis+=ww[i].xx?0:mid;
        sbd();
        if(md>=dd)l=mid+1,ans=sb-dd*mid;
        else r=mid-1;
        for(int i=1;i<=m;i++)ww[i].dis-=ww[i].xx?0:mid;
  }
  cout<<ans;
    return 0;
}

原创粉丝点击