Luogu P2279 [HNOI2003]消防局的設立
阿新 • • 發佈:2018-07-27
ons 怎麽 ctype 簡單的 type ble hnoi digi 樹形dp ,還可以多往上覆蓋一些點。
這真的是一道SB題。去你的樹形DP
我們看到題目就開始考慮貪心,怎麽搞?
一個顯然的思路,每次找出一個深度最大且未被覆蓋的點,然後建一個消防局?
但這樣的話,動用簡單的人類思維就可以知道:我TM的還不如放在它爺爺(父節點的父節點)處呢
然後考慮有沒有反例。。。。。。30min later
怎麽一個反例都沒有?那麽說明這個貪心是正確的了?
接下來我們來簡單證明一下貪心的正確性:
由於這個點是當前深度最大且未被覆蓋的點,因此所有深度比它深的點都被覆蓋了。
然後沒有被覆蓋且可以被這個點覆蓋的點還有以下幾類:
- 它自己(or 兄弟)
- 它的父親節點
- 它的爺爺節點
然後我們很容易發現,當把消防站設立在它爺爺處不僅可以覆蓋到所有1,2,3情況的點
然後我們直接貪即可。由於這裏我們可以通過BFS的順序直接得出這些點的深度(這樣就直接排好序了)
最後一個一個彈出判斷即可,復雜度\(O(N)\)。
CODE
#include<cstdio> #include<cstring> #include<cctype> using namespace std; const int N=1005; struct edge { int to,next; }e[N<<1]; int head[N],cnt,n,x,father[N],q[N],ans; bool vis[N]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-‘0‘,isdigit(ch=tc())); } inline void double_add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; e[++cnt].to=x; e[cnt].next=head[y]; head[y]=cnt; } inline void BFS(int x) { register int i,H=0,T=1; q[1]=x; vis[x]=1; while (H<T) { int now=q[++H]; for (i=head[now];~i;i=e[i].next) if (!vis[e[i].to]) q[++T]=e[i].to,vis[e[i].to]=1; } } inline void reset(int now,int d) { if (d>2) return; vis[now]=1; for (register int i=head[now];~i;i=e[i].next) reset(e[i].to,d+1); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; read(n); memset(head,-1,sizeof(head)); for (i=2;i<=n;++i) read(x),double_add(i,x),father[i]=x; BFS(1); memset(vis,0,sizeof(vis)); father[1]=1; while (n) { if (!vis[q[n]]) ++ans,reset(father[father[q[n]]],0); --n; } return printf("%d",ans),0; }
Luogu P2279 [HNOI2003]消防局的設立