[CodeForces][貪心][樹形結構]CF980E The Number Games
阿新 • • 發佈:2020-09-08
題面
看到樹形結構我們可能會想到樹形 \(DP\),但是仔細研究過後我們會發現這題是不需要 \(DP\) 的。
觀察每個點的貢獻為 \(2^i\) ( \(i\) 為節點標號 ),也就是說,選擇一個標號更大的點一定比選擇幾個標號小的點優。
這時我們貪心的思路就明朗了:
從 \(n\ -\ 1\) 遍歷每個點,被選擇的點之間的路徑都需要連線,只要一個點路徑上需要新增的點加起來不超過限制,就加入該點 ( 以及路徑上的點 )。
至於路徑,我們只需要用 \(DFS\) 序維護,然後用差分樹狀陣列暴力維護一下一個點到根節點的路徑長度即可
程式碼:
// 貪心的考慮:2^n 一定保留 // DFS 暴力跳點 # include <iostream> # include <cstdio> # define MAXN 1000005 # define fa(x) nd[x].fa # define dep(x) nd[x].dep # define siz(x) nd[x].siz # define dfn(x) nd[x].dfn struct edge{ int v, next; }e[MAXN<<1]; struct node{ int fa, dep, siz, dfn; }nd[MAXN]; int cntS, lim; int hd[MAXN], cntE, bit[MAXN]; bool sel[MAXN]; int cntSel; void AddE(int u, int v); void DFS(int now, int fa); int Lowbit(int x); void Update(int pos, int val); int GetSum(int pos); int main(){ int n, k; scanf("%d%d", &n, &k); k = n - k; // 刪除點數轉化為保留點數 lim = n; for(int i = 1, u, v; i <= n-1; i++){ scanf("%d%d", &u, &v); AddE(u, v); AddE(v, u); } DFS(n, 0); sel[n] = 1; for(int i = n; i >= 1; i--){ if((!sel[i]) && (dep(i)-GetSum(dfn(i))+cntSel <= k)){ // 要求選擇當前點後新增點數不超過總保留點數 for(int now = i; now != n; now = fa(now)){ if(sel[now]){ break; } else{ sel[now] = 1; Update(dfn(now), 1); Update(dfn(now)+siz(now), -1); // 差分 cntSel++; } } } } for(int i = 1; i <= n; i++){ if(!sel[i]){ printf("%d ", i); } } return 0; } int GetSum(int pos){ int ans = 0; while(pos){ ans += bit[pos]; pos -= Lowbit(pos); } return ans; } void Update(int pos, int val){ while(pos <= lim){ bit[pos] += val; pos += Lowbit(pos); } } int Lowbit(int x){ return x & (-x); } void DFS(int now, int fa){ fa(now) = fa, dep(now) = dep(fa) + 1, siz(now) = 1, dfn(now) = ++cntS; for(int i = hd[now]; i; i = e[i].next){ if(e[i].v == fa){ continue; } DFS(e[i].v, now); siz(now) += siz(e[i].v); } } void AddE(int u, int v){ e[++cntE] = (edge){v, hd[u]}; hd[u] = cntE; }