codeforces 700B Connecting Universities (貪心詳解)
阿新 • • 發佈:2019-01-31
codeforces 700B Connecting Universities
結論思路:
這個題一眼看上去很難,但是正著做不行,我們換個角度:考慮每條邊的貢獻。
因為是一棵樹,所以一條邊把樹分成兩個集合,假如左邊有x個學校,右邊有y個學校。
貪心地想,讓每條邊在學校的路徑上最多,所以貢獻為min(x,y)
具體實現:一次dfs即可,複雜度O(N)
聽起來很有道理,可我不會證呀~~
然後就自己搞了一個貪心
對於一個點對(u,v),它的貢獻是dep[u] + dep[v] - 2 * dep[lca]。
所以最終的ans就是singma (dep[i]) - 2 * singma(dep[lca])
前一部分是一定的,所以我們只需要讓k個lca的dep最小。
隨意選擇一個點為根,從上到下貪心的處理,儘量多的作為lca。
對於每個點,我們維護siz,siz[i]表示i的子樹裡面待選擇點的個數。
我們找出一個點u所有子樹中siz最大的一個子樹v,有maxx個待配點,如果其他兒子的siz加起來大於maxx,
那麼我們一定有辦法讓所有點都與它子樹外的點配對,也就是說我們可以讓所有點對的lca都是u。
這樣我們就計算完了所有的貢獻。
如果無發做到這一點,那麼顯然我們把所有v外的點都與v內的點配對。
(如果不這樣v中會剩下更多的點)
而且為了保證我們之後的最優決策,我們儘可能的找v中最大子樹中的點配對。
(為什麼呢??這樣一來,如果進入子問題之後其他兒子的siz加起來大於maxx,問題也就結束了。
不然的話,儘量配maxx也是最優的。)
然後就是一個遞迴判斷。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
#define N 200010
using namespace std;
int n, k, opt, idc=0;
int head[N], flag[N], maxx[N], maxcnt[N];
LL siz[N], dep[N];
LL sum = 0;
inline int read(){
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct Edge{
int to, nxt;
}ed[N << 1];
inline void adde(int u, int v){
ed[++idc].to = v;
ed[idc].nxt = head[u];
head[u] = idc;
}
inline void dfs(int u, int f){
if( flag[u] ) siz[u]++, sum += dep[u];
for(int i=head[u]; i; i=ed[i].nxt){
int v = ed[i].to;
if(v == f) continue;
dep[v] = dep[u] + 1;
dfs(v, u);
siz[u] += siz[v];
if(maxx[u] < siz[v]) maxx[u] = siz[v], maxcnt[u] = 1;//最大子樹大小
else if(maxx[u] == siz[v]) maxcnt[u]++;//最大子樹個數
}
}
inline void solve(int u, int f){
if( !siz[u] ) return ;
if( flag[u] ){//本身是特殊點(與最大子樹中一個點匹配)
siz[u] -= 2;
if(maxcnt[u] == 1) maxx[u]--;
sum -= dep[u] << 1;
}
if(siz[u] - maxx[u] >= maxx[u]){
sum -= siz[u] * dep[u];
return;
}
else {
sum -= (siz[u] - maxx[u]) * dep[u] << 1;
for(int i=head[u]; i; i=ed[i].nxt){
int v = ed[i].to;
if(v == f) continue;
if(siz[v] >= maxx[u] + (flag[u] && maxcnt[u] == 1)){
maxx[v] -= siz[v] - (maxx[u] - (siz[u] - maxx[u]));
maxx[v] = max(maxx[v], 0);
siz[v] = (maxx[u] - (siz[u] - maxx[u]));
solve(v, u);
return;
}
}
}
}
int main(){
freopen ("beauty.in", "r", stdin);
freopen ("beauty.out", "w", stdout);
scanf("%d%d%d", &n, &k, &opt);
for(int i=1; i<=k; i++){
int a = read(), b = read();
flag[a] = flag[b] = 1;
}
for(int i=1; i<n; i++){
int u = read(), v = read();
adde(u, v); adde(v, u);
}
dfs(1, 1);
solve(1, 1);
cout << sum << endl;
}