BZOJ2654 Tree

2654: tree

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 3077   Solved: 1293
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

2 2 1
0 1 1 1
0 1 2 0

Sample Output

2

HINT

原数据出错,现已更新 by liutian,但未重测---2016.6.24


解析:

       二分答案+最小生成树。

       做这道有一种错路的思路,即按边的种类为第一关键字,边的权值为第二关键字排序,然后做最小生成树,

先取边need条后再跳过去取黑边。反例很好举,如两点之间相连的白边权值较小但未取到,而黑边权值非常大

,这样得到的答案就不是最优的。

       这道题的正解个人感觉有点奇怪。我们考虑让白边变得更重要(不重要),所以可以给白边都加上(减去)

一个值num,我们二分这个值,带入验证,最终的答案为sum-num*need。


代码:

#include <bits/stdc++.h>
using namespace std;

const int Max=100010;
int n,m,k,ans;
int father[50100];
struct shu{int x,y,len,flag;};
shu edge[Max];

inline int get_int()
{
   int x=0,f=1;
   char c;
   for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
   if(c=='-') {f=-1;c=getchar();};
   for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
   return x*f;
}

inline bool comp(const shu &a,const shu &b)
{
   return a.len == b.len ? a.flag < b.flag : a.len < b.len;
}

inline int getfather(int v)
{
   return father[v]==v ? v :father[v]=getfather(father[v]);
}

inline int check(int num)
{
   shu bian[Max];
   memcpy(bian,edge,sizeof(edge));
   for(int i=1;i<=n;i++) father[i]=i;
   for(int i=1;i<=m;i++) if(!bian[i].flag) bian[i].len+=num;
   sort(bian+1,bian+m+1,comp);
   int s=0,minn=0,x=0;
   for(int i=1;i<=m;i++)
   {
   	 int fax=getfather(bian[i].x),fay=getfather(bian[i].y);
   	 if(fax != fay)
   	 {
   	   s++;
   	   minn+=bian[i].len;
   	   father[fay]=fax;
   	   if(!bian[i].flag) x++;
   	 }
   	 if(s == n-1) break;
   }
   if(x >= k) {ans=minn-k*num;return 1;}
   else return 0;
}

int main()
{
   //freopen("tree.in","r",stdin);
   n=get_int();
   m=get_int();
   k=get_int();
   for(int i=1;i<=m;i++)
   {
   	 edge[i].x=get_int()+1;
   	 edge[i].y=get_int()+1;
   	 edge[i].len=get_int();
   	 edge[i].flag=get_int();
   }

   int l=-100,r=100,mid;
   while(l ^ r)
   {
     mid = l + r >> 1;
     if (check(mid)) l = mid + 1;
     else r = mid;
   }
   cout<<ans<<"\n";
   return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_38083668/article/details/81026706