1. 程式人生 > >codeforces 700B Connecting Universities (貪心詳解)

codeforces 700B Connecting Universities (貪心詳解)

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; }