[國家集訓隊2012]tree(陳立傑)


[國家集訓隊2012]tree(陳立傑)

題目

給你一個無向帶權連通圖,每條邊是黑色或白色。讓你求一棵最小權的恰好有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

OUTPUT

2

數據規模

0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有數據邊權為[1,100]中的正整數。

解題報告

國家集訓隊的題,果然是好題= =

首先,我們觀察題面,顯然與最小生成樹有什么關系,但是,直接跑最小生成樹顯然也是不合理的,那么問題就在於,如何在跑最小生成樹的同時,還能保證白邊的個數?

正解是個很神奇的東西——二分

首先我們想怎么二分,我們注意到,邊權的范圍很小,是$[1,100]$,那么,我們是否可以通過控制白邊的邊權,達到在最小生成樹中控制白邊的數量呢?

顯然可以。

我們以$Kruskal$算法為例,我們進行$Kruskal$時,是以每條邊的邊權為依據,進行從小到大排序,然后從小到大取出各個邊,不斷加入連通分量中,最后形成最小生成樹。那么,當我們改變某一些邊的權值時,我們按權值排序得出的邊的序列也一定就會不一樣,那么,我們就可以通過控制權值來控制加入生成樹的白邊數了。

具體做法:

在$[-100,100]$中二分得到$mid$,讓所有白邊的權值加上該$mid$值,跑$Kruskal$,直到得到最終結果

但是,只是這樣就可以了嗎?

顯然不是。

我們考慮,假如我們點很少,只有$10+$個點,但是我們的邊很多,達到了$100000$,而且權值范圍還被限制在了$[1,100]$,那么顯然,會有許多等價的黑邊與白邊,即使加上了某一個權值,也可能會有很多白邊與很多黑邊相等價,當我們按照權值排序的同時,我們把黑邊與白邊混在了一起,然后我們就開始了$Kruskal$,那樣的話,我們本來可以得到剛好$need$條白邊,我們卻把一些與黑邊等價的白邊扔進了生成樹中,這樣的話,我們本來可以得到最優解,卻認為它是不合法的。

這時我們就需要處理一下這些等價的邊。

具體做法:

在二分后判斷時,我們不單單判斷此時白邊的數目是否等於$need$,而是將一切白邊數大於等於$need$的情況全部考慮上,然后,我們加上了(或者是減去,因為我們有負數)許多為了控制生成樹的權值,所以我們需要減去(或加上)這些權值,我們並不能將所有白邊修改的權值修改回去,而是將$need\times mid$權值處理掉,因為假如我們處理所有的白邊,我們可能將那些等價於黑邊的白邊也處理掉了,也就是相當於處理了黑邊,顯然是不合法的,所以我們只處理$need$條,也就是真正被當成白邊的白邊數量

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 using namespace std;
 6 inline int read(){
 7     int sum(0),f(1);
 8     char ch(getchar());
 9     for(;ch<'0'||ch>'9';ch=getchar())
10         if(ch=='-')
11             f=-1;
12     for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
13     return sum*f;
14 }
15 struct edge{
16     int s,e,w,col,tmp;
17     friend bool operator<(const edge &a,const edge &b){
18         return a.tmp==b.tmp?a.col<b.col:a.tmp<b.tmp;
19     }
20 }a[100005];
21 int n,m,need;
22 int fa[50005];
23 inline int find(int x){
24     if(fa[x]==x)
25         return x;
26     fa[x]=find(fa[x]);
27     return fa[x];
28 }
29 int tp;
30 inline bool krus(){
31     tp=0;
32     int tot(0),whit(0);
33     sort(a+1,a+m+1);
34     for(int i=1;i<=m;++i){
35         int s(a[i].s),e(a[i].e);
36         int fs(find(s)),fe(find(e));
37         if(fs!=fe){
38             fa[fe]=fs;
39             if(a[i].col==0)
40                 ++whit;
41             ++tot;
42             tp+=a[i].tmp;
43             if(tot==n-1)
44                 break;
45         }
46     }
47     return whit>=need;
48 }
49 inline int gg(){
50     freopen("nt2012_tree.in","r",stdin);
51     freopen("nt2012_tree.out","w",stdout);
52     n=read(),m=read(),need=read();
53     for(int i=1;i<=m;++i)
54         a[i].s=read()+1,a[i].e=read()+1,a[i].w=read(),a[i].col=read();
55     int l(-105),r(105),ans;
56     while(l<=r){
57         int mid((l+r)>>1);
58         for(int i=1;i<=n;++i)
59             fa[i]=i;
60         for(int i=1;i<=m;++i){
61             if(a[i].col==0)
62                 a[i].tmp=a[i].w+mid;
63             else
64                 a[i].tmp=a[i].w;
65         }
66         if(krus())
67             l=mid+1,ans=tp-need*mid;
68         else
69             r=mid-1;
70     }
71     printf("%d",ans);
72     return 0;
73 }
74 int K(gg());
75 int main(){;}
View Code

 


免責聲明!

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



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