1. 程式人生 > 其它 >BZOJ2654 tree (wqs二分)

BZOJ2654 tree (wqs二分)

題目描述

給你一個無向帶權連通圖,每條邊是黑色或白色。讓你求一棵最小權的恰好有need條白色邊的生成樹。 題目保證有解。   一個最小生成樹問題,但是我們要選need條白邊,我們用g(i)表示選取i條白邊的最優方案(生成樹的權值最小),那麼可以大致猜出g(i)是關於i的一個下凸函式,可以發現斜率k是有單調性的,我們二分這個斜率k,相當於給每條白邊的權值加上一個k,統計數量use,如果use>=need,說明k小了,要增大,同理,use<need,要減小k。

那麼問題來了,如果說當前白邊加上mid後,白邊條數use>need了,如果加上mid+1後,use<need了要怎麼辦?

題目中說到了:保證有解,所以出現上述情況時一定有黑邊==白邊的邊權

所以我們只需要把一條黑邊換成白邊就好,即我們排序時如果黑邊權值等於白邊,則白邊優先。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 50001
 4 #define M 100001
 5 int n,m,k;
 6 struct node{
 7     int u,v,w,c;
 8 }e[M];
 9 int use,sum,fa[N];
10 bool cmp(node a,node b){
11     if(a.w!=b.w) return
a.w<b.w; 12 return a.c<b.c;//黑邊白邊權值一樣,優先選白邊 13 } 14 int find(int x){ 15 return fa[x]==x?x:fa[x]=find(fa[x]); 16 } 17 void check(int x){ 18 for(int i=1;i<=m;i++) 19 if(e[i].c==0) e[i].w+=x;//白邊加權值 20 sort(e+1,e+m+1,cmp); 21 for(int i=1;i<=n;i++) fa[i]=i; 22 int
now=0,fu,fv,j=1; 23 use=0,sum=0; 24 while(now!=n-1){ 25 fu=find(e[j].u);fv=find(e[j].v); 26 if(fu!=fv){ 27 now++; 28 fa[fu]=fv; 29 if(e[j].c==0) ++use;//統計使用的白邊數量 30 sum+=e[j].w; 31 } 32 ++j; 33 } 34 for(int i=1;i<=m;i++) 35 if(e[i].c==0) e[i].w-=x;//還原 36 } 37 38 int main() 39 { 40 scanf("%d%d%d",&n,&m,&k); 41 for(int i=1;i<=m;i++){ 42 scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].c); 43 ++e[i].u;++e[i].v;//題目是編號從0開始,要+1 44 } 45 int l=-101,r=101,mid,ans;//值域[1,100]; 46 while(l<=r){ 47 mid=l+r>>1;check(mid); 48 if(use>=k){ 49 ans=sum-k*mid; 50 l=mid+1; 51 } 52 else r=mid-1; 53 } 54 cout<<ans; 55 return 0; 56 }