1. 程式人生 > >[CODE FESTIVAL 2016]Problem on Tree

[CODE FESTIVAL 2016]Problem on Tree

題意:給一棵樹,對於一個滿足以下要求的序列$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]);
}