[CODE FESTIVAL 2016]Problem on Tree
阿新 • • 發佈:2018-11-06
題意:給一棵樹,對於一個滿足以下要求的序列$v_{1\cdots m}$,求最大的$m$
對$\forall1\leq i\lt m$,路徑$(v_i,v_{i+1})$不包含$v$中除了$v_i,v_{i+1}$以外的任何點
這個題好神啊...根本做不動
如果一個度數$\geq3$的點$x\in v$,那麼它的$\geq3$個子樹中只能有至多$2$個子樹包含$v$中節點,否則矛盾,所以我們可以找它的一個不包含$v$中節點的子樹,並把這個節點移到這個子樹中的一個葉子裡,移動後$v$仍然滿足要求
所以最優解只能包含度數$\leq2$的點
先對葉子討論,可以證明最優解一定包含葉子:如果一個葉子$x$不在$v$內,那麼我們從$x$開始走,如果走到度數$=2$的節點$y$,如果$y\in v$,直接把它移到$x$即可,否則繼續往外走;如果走到度數$\geq3$的點$y$,如果它只有一個子樹包含$v$中節點,那麼繼續往那個子樹走,否則可以直接把$x$在合適的位置插入$v$中並使得$v$仍然滿足要求
最後討論度數$=2$的點,如果一個度數$=2$的點$x\in v$,把它刪掉後樹被分成兩部分,一部分的點在$v$中的位置在$x$之前,另一部分的點在$v$中的位置在$x$之後,所以所有被選擇的度數$=2$的點一定在一條鏈上
於是DP找出包含最多度數$=2$的點的一條鏈,這些點的數量加上葉子個數就是答案
#include<stdio.h> #include<algorithm> using namespace std; int h[100010],nex[200010],to[200010],M; void add(int a,int b){ M++; to[M]=b; nex[M]=h[a]; h[a]=M; } int d[100010],v[100010],f[100010],g[100010]; void dfs(int fa,int x){ f[x]=g[x]=v[x]; for(int i=h[x];i;i=nex[i]){ if(to[i]!=fa){ dfs(x,to[i]); f[x]=max(f[x],max(g[x]+g[to[i]],f[to[i]])); g[x]=max(g[x],g[to[i]]+v[x]); } } } int main(){ int n,i,x,y,s; scanf("%d",&n); for(i=1;i<n;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); d[x]++; d[y]++; } s=0; for(i=1;i<=n;i++){ s+=d[i]==1; v[i]=d[i]==2; } dfs(0,1); printf("%d",s+f[1]); }