[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]);
}
posted @ 2018-11-06 10:14 jefflyy 阅读(...) 评论(...) 编辑 收藏