hdu5758 2016 Multi-University Training Contest 3 Explorer Bo 解题报告
2016-09-15 21:53
417 查看
感觉最近都没状态不知道为什么。。。。一道不难的题不会做,看了题解发现自己好蠢。。。。
题意:给一颗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;
}
题意:给一颗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;
}
相关文章推荐
- hdu5755 2016 Multi-University Training Contest 3 Gambler Bo 解题报告
- hdu5753 2016 Multi-University Training Contest 3 Permutation Bo 解题报告
- hdu5756 2016 Multi-University Training Contest 3 Boss Bo 解题报告
- hdu5754 2016 Multi-University Training Contest 3 Life Winner Bo 解题报告
- hdu5752 2016 Multi-University Training Contest 3 Sqrt Bo 解题报告
- hdu 5759 2016 Multi-University Training Contest 3 Gardener Bo 解题报告
- 2016 Multi-University Training Contest 6 解题报告
- 【解题报告】2016 Multi-University Training Contest 3
- hdu 5821 2016 Multi-University Training Contest 8 Ball 解题报告
- 【解题报告】2016 Multi-University Training Contest 4
- 2016 Multi-University Training Contest 5 解题报告
- hdu 5828 2016 Multi-University Training Contest 8 Rikka with Sequence 解题报告
- 2016 Multi-University Training Contest 1----解题报告
- 2016 Multi-University Training Contest 4 解题报告
- 2016 Multi-University Training Contest 2----解题报告
- 2016 Multi-University Training Contest 3----解题报告
- hdu 5730 2016 Multi-University Training Contest 1 Shell Necklace 解题报告
- hdu 5823 2016 Multi-University Training Contest 8 color II 解题报告
- 2016 Multi-University Training Contest 3 解题报告
- [HDU5756] Boss Bo [2016 Multi-University Training Contest 3(2016多校联合训练3) E]