hdu5758 2016 Multi-University Training Contest 3 Explorer Bo 解题报告

31 篇文章 0 订阅

感觉最近都没状态不知道为什么。。。。一道不难的题不会做,看了题解发现自己好蠢。。。。



题意:给一颗n个节点的树,问你用最少的链覆盖这棵树上所有的边(可以重复覆盖),链的长度和最小为多少

链的长度=覆盖的边的数量




题解:

树形DP


首先,如果要覆盖,最少需要(n+1)/2条链,链的两端肯定是2个叶子节点,如果叶子节点数是奇数,那么有一条链由一个叶子向上覆盖他父亲连向他的边就停止


那么,定义f[x]为以x为根的子树内用最少的链覆盖,链的最小长度和,

首先一棵子树里的叶子肯定先两两匹配,再考虑和子树根的父亲的连边

如果他的一个孩子y的子树内叶子节点数为奇数,就一定有1条链连上来(为了覆盖x和y的边),对f[x]贡献f[y]+1

如果他的一个孩子y的子树内叶子节点数为偶数,就一定有2条链连上来(首先为了覆盖x和y的边,至少要有一条链,那这个叶子连上来,剩余的原来和他匹配的点没有点匹配,故也连一条链上来),对f[x]贡献f[y]+2


最后,求完整棵树的f后,对整棵树考虑,

如果整棵树的叶子节点数是偶数,可以直接输出根的f值

如果是奇数,那么会有一条链叶子连上来的链没有叶子匹配,事实上这条链也不用连上来,只要连向这个叶子的父亲即可,所以对于这种情况,我们要撤销一条链,考虑撤销一条链的影响,

从这个叶子走上去,如果x到y这条边之前只被这一条链覆盖,撤消后无边覆盖,就要像上面的情况一样,走两条边上去,答案+1,

如果这条边之前被覆盖了2次,那么撤销了没有影响,答案-1,遍历整棵树找到使答案最小的就行了



注意一个地方,就是如果你选的根是一个叶子节点,那整棵树的叶子节点数要+1,根是不能例外的



code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<string>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;

const int maxn = 110000;
struct edge
{
	int y,next;
}a[maxn<<1]; int len,first[maxn];
LL f[maxn];
int siz[maxn];
int n,m;

void ins( int x,int y )
{
	len++;
	a[len].y = y;
	a[len].next = first[x]; first[x] = len;
}
void dfs( int x,int fa )
{
	bool flag = true;
	siz[x] = 0; f[x] = 0;
	for( int k=first[x];k;k=a[k].next )
	{	
		int y = a[k].y;
		if( y != fa )
		{
			dfs( y,x );
			flag = false;
			siz[x] += siz[y];
			if( siz[y]&1 ) f[x] += f[y]+1;
			else f[x] += f[y]+2;
		}
	}
	if( flag ) siz[x] = 1;
}
LL fm( int x,int fa )
{
	LL mx = 0;
	for( int k=first[x];k;k=a[k].next )
	{
		int y = a[k].y;
		if( y != fa )
		{
			LL temp = fm( y,x );
			temp += (siz[y]&1)?-1:1;
			if( temp > mx ) mx = temp;
		}
	}
	return mx;
}
int main()
{
	int t;
	scanf("%d",&t);
	while( t-- )
	{
		memset( first,0,sizeof first ); len = 0;	
		int x,y;
		scanf("%d",&n);
		for( int i=1;i<n;i++ )
		{
			scanf("%d%d",&x,&y);
			ins( x,y ); ins( y,x );
		}
		dfs( 1,0 );
		if( a[first[1]].next == 0 ) siz[1]++;
		if( siz[1]&1 )	f[1] -= fm( 1,0 );
		printf("%lld\n",f[1]);
		
	}	
	
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值