1. 程式人生 > >將軍令

將軍令

ash using 得出 namespace ati dash sta size int

題目鏈接:https://www.luogu.org/problemnew/show/P3942

 題解:

  當k一定且很小(1或2)時,明顯這就成了一道樹形dp。也就是說如果你寫過HNOI2003消防局的設立的話這道題就可以至少拿75分。(或者你花上幾個小時推出+調試k=3時的dp方程就可以拿到90分啦qaq)

  但是這道題顯然不是這麽做的。其實我們只要稍稍貪心一下即可。一個明顯的性質是在能控制到一個節點的情況下,小隊的深度越淺越好。(因為這樣它就可以控制更多的點)。

  我們先任選一個節點為根,把無根樹轉化成有根樹。然後遍歷整棵樹,維護子節點對父親節點的要求。再以子節點的信息更新父親節點即可。

  我們設v[x]表示節點x上方距離v[x]一定要有一個小隊。(當v[x]==1時就是x節點的父親節點一定要是小隊)。那麽比較容易發現的更新條件就是:葉子節點的v[]為k;當一個節點的子節點中有一個的v[]為1時該節點就一定是小隊;一個小隊節點的v[]為(2*k+1);不滿足上述條件時v[x]=min{v[y]|y是x的子節點}-1。

  但是其實上述思路是有漏洞的。比如當k=1時,其左孩子的v[]為2,其右孩子的v[]為3時,其實該節點的v[]值為2。(因為他左子樹中未被覆蓋的點可以被右子樹覆蓋)。我放張圖大家理解一下:

技術分享圖片

  運用數學歸納法(其實就是找規律qwq)可以得出的結論是:對於節點x的子節點y來說,若v[y]+maxx>=2*k+3(maxx為x節點的子節點中最大的v[]值),則該子節點y不需被考慮。

  另外,當n=0時該方法無法處理,所以我加了個特判。(當然你也可以在dfs時處理,但加個特判顯然更方便=-=)。

技術分享圖片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define LL long long
#define RI register int
using namespace std;
const int INF = 0x7ffffff ;
const int N = 1e5 + 10
; inline int read() { int k = 0 , f = 1 ; char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == -) f = -1 ; for( ; isdigit(c) ; c = getchar()) k = k*10 + c-0 ; return k*f ; } struct Edge { int to, next ; }e[N<<1] ; int n, k, t, ans = 0 ; int head[N] ; inline void add_edge(int x,int y) { static int cnt = 0 ; e[++cnt].to = y, e[cnt].next = head[x], head[x] = cnt ; } int v[N] ; // x:上面距離x位置一定要有一個小隊 void dfs(int x,int f) { int y ; vector<int>hh ; for(int i=head[x];i;i=e[i].next) { y = e[i].to ; if(y == f) continue ; dfs(y,x) ; } int mi = INF, mx = -INF ; for(int i=head[x];i;i=e[i].next) { y = e[i].to ; if(y == f) continue ; hh.push_back(v[y]) ; mx = max(mx,v[y]), mi = min(mi,v[y]) ; } if(mi == INF) { v[x] = k ; return ; } sort(hh.begin(),hh.end()) ; int mm = hh.size(), i ; for(i=0;i<mm;i++) { if(hh[i]+mx < (k<<1)+3) break ; } if(i == mm) { v[x] = mx-1 ; } else if(f == 0) { ans ++ ; } else if(hh[i] == 1) { ans ++ ; v[x] = (k<<1)+1 ; } else v[x] = hh[i]-1 ; } int main() { n = read(), k = read(), t = read() ; if(!k) { printf("%d",n) ; return 0 ; } int x, y ; for(int i=1;i<n;i++) { x = read(), y = read() ; add_edge(x,y) ; add_edge(y,x) ; } dfs(1,0) ; printf("%d",ans) ; return 0 ; }
View Code

——end ;

將軍令