[BZOJ 2654]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

V<=50000,E<=100000,所有數據邊權為[1,100]中的正整數。

題解

二分+$kruskal$

如果直接$kruskal$求最小生成樹,是無法保證白邊數量的,那么我們考慮如果改變白邊的數量。我們可以把白邊全部都加上一個權值,也就是我們二分的值,然后跑最小生成樹,同時記錄白邊數量。當白邊數量>=$need$時,$l=mid+1$,否則$r=mid−1$,更新答案就是這棵生成樹的權值和減去所有白邊的增量。

證明:
我們發現,如果我們給白邊增加權值,做最小生成樹,由於白邊權值增大,導致不容易選白邊。記$f(x)$為給白邊增加$x$($x$可為負)權值,做最小生成樹后,選白邊的數量。可以發現,$f(x)$隨$x$增大而減小,顯然可以二分。
其次,我們發現,由於黑邊的權值是不變的,與白邊權值不相互影響。同樣由於白邊之間關系相對不變,必然選出的$need$條白邊一定是符合題意的。

 1 #include<map>
 2 #include<ctime>
 3 #include<cmath>
 4 #include<queue>
 5 #include<stack>
 6 #include<cstdio>
 7 #include<string>
 8 #include<vector>
 9 #include<cstring>
10 #include<cstdlib>
11 #include<iostream>
12 #include<algorithm>
13 #define LL long long
14 #define RE register
15 #define IL inline
16 using namespace std;
17 const int V=50000;
18 const int E=100000;
19 
20 int mid;
21 int v,e,need,ans,cnt,tmp;
22 struct tt
23 {
24     int u,v,c,col;
25 }edge[E+5];
26 
27 IL int Kruskal();
28 bool comp(const tt &a,const tt &b) {return a.c+(a.col^1)*mid<b.c+(b.col^1)*mid;}
29 
30 int set[V+5];
31 IL int find(int r) {return set[r]!=-1 ? set[r]=find(set[r]):r;}
32 
33 int main()
34 {
35     scanf("%d%d%d",&v,&e,&need);
36     for  (RE int i=1;i<=e;i++) scanf("%d%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c,&edge[i].col);
37     int l=-100,r=100;
38     while (l<=r)
39     {
40         mid=(l+r)>>1;
41         if (Kruskal()>=need) l=mid+1,ans=tmp;
42         else r=mid-1;
43     }
44     printf("%d\n",ans);
45     return 0;
46 }
47 
48 IL int Kruskal()
49 {
50     tmp=cnt=0;
51     int k=0;
52     memset(set,-1,sizeof(set));
53     sort(edge+1,edge+1+e,comp);
54     for (RE int i=1;i<=e;i++)
55     {
56         int q=find(edge[i].u);
57         int p=find(edge[i].v);
58         if (p!=q)
59         {
60             k+=edge[i].col^1;
61             set[q]=p;
62             cnt++;
63             tmp+=edge[i].c;
64             if (cnt==v-1) break;
65         }
66     }
67     return k;
68 }
BZOJ能過的解法

感謝Hzoi_Maple

由於$COGS$數據會有不滿足恰好$need$條白邊的情況

打個比方有這樣的數據:加$0$時大於$need$,加$1$就小於$need$了。

這樣應該在跑最小生成樹的時候把所有的白邊都加上加的那個權值,結果就是最小生成樹的權值和減去$need*$加上的權值,多出來的那一部分完全可以當做黑邊來看,因為數據是$100000$的,這樣就可以了。(來自Hzoi_Maple

排序的時候,如果邊權相同,要把白邊放在前面。

要計算當前至多能取多少白邊,當然要把白邊放前面。由於保證有解,在$cnt>=need$且$cnt$取最小值的方案下,一定能有黑邊把多余的白邊代替掉。

 1 #include<map>
 2 #include<ctime>
 3 #include<cmath>
 4 #include<queue>
 5 #include<stack>
 6 #include<cstdio>
 7 #include<string>
 8 #include<vector>
 9 #include<cstring>
10 #include<cstdlib>
11 #include<iostream>
12 #include<algorithm>
13 #define LL long long
14 #define RE register
15 #define IL inline
16 using namespace std;
17 const int V=50000;
18 const int E=100000;
19 
20 int mid;
21 int v,e,need,ans,cnt,tmp;
22 struct tt
23 {
24     int u,v,c,col,rc;
25 }edge[E+5];
26 
27 IL int Kruskal();
28 IL void change();
29 bool comp(const tt &a,const tt &b) {return a.rc==b.rc ? a.col<b.col:a.rc<b.rc;}
30 
31 int set[V+5];
32 IL int find(int r) {return set[r]!=-1 ? set[r]=find(set[r]):r;}
33 
34 int main()
35 {
36     scanf("%d%d%d",&v,&e,&need);
37     for  (RE int i=1;i<=e;i++) scanf("%d%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c,&edge[i].col);
38     int l=-100,r=100;
39     while (l<=r)
40     {
41         mid=(l+r)>>1;
42         if (Kruskal()>=need) l=mid+1,ans=tmp-need*mid;
43         else r=mid-1;
44     }
45     printf("%d\n",ans);
46     return 0;
47 }
48 
49 IL void change()
50 {
51     for (RE int i=1;i<=e;i++) edge[i].rc=edge[i].c+(edge[i].col^1)*mid;
52 }
53 IL int Kruskal()
54 {
55     change();
56     tmp=cnt=0;
57     int k=0;
58     memset(set,-1,sizeof(set));
59     sort(edge+1,edge+1+e,comp);
60     for (RE int i=1;i<=e;i++)
61     {
62         int q=find(edge[i].u);
63         int p=find(edge[i].v);
64         if (p!=q)
65         {
66             k+=edge[i].col^1;
67             set[q]=p;
68             cnt++;
69             tmp+=edge[i].rc;
70             if (cnt==v-1) break;
71         }
72     }
73     return k;
74 }
COGS能過的解法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM