题目:
样例输入:
2
2
1 2
4
1 2
2 3
3 4
样例输出:
0
1
题意:给定一棵有n个节点的树,其中n是偶数,我们可以删掉树的一些边,问我们有多少种删边方案使得我们删完边后形成的所有连通子图的顶点个数均为偶数。其中删的边数至少为1.
分析:首先我们先对树进行一遍遍历求得以每个点为根的子树中的节点个数,我们看看有多少个子树的节点个数是偶数,那么如果我们把他的父边删除那么就会形成一个顶点个数是偶数的连通子图,当然,子图如果作后续分解我们就不需要单独讨论了,如果子树中节点个数是偶数,那么我们可以选择删除父边,也可以选择不删除父边,所以就是说对于每个以该节点为根的子树的节点个数是偶数的节点我们都有两种选择,但是需要注意的一点是删除父边的前提是父边存在,其实我们可以假设1号节点是根节点,那么唯一不存在父边的节点也就是根节点了,我们直接特判一下就好了,计算一下有多少个节点满足以该节点为根的子树中的节点个数是偶数(不算真正的根节点),每个根节点我们有两种选择,所以就是多少个2相乘,需要注意的一点是所有节点都不删边的情况我们需要去除掉,也就是整棵树作为一个连通图的情况,就是对答案减1即可。
细节见代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=4e5+10,mod=998244353;
int h[N],ne[N],e[N],idx;
int cnt[N];
long long ans;
void add(int x,int y)
{
e[idx]=y;
ne[idx]=h[x];
h[x]=idx++;
}
void dfs(int x,int fa)
{
cnt[x]=1;
for(int i=h[x];i!=-1;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
dfs(j,x);
cnt[x]+=cnt[j];
}
if(x!=1&&cnt[x]%2==0) ans=(ans*2)%mod;
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
scanf("%d",&n);
idx=0;ans=1;
for(int i=1;i<=n;i++) h[i]=-1;
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(1,-1);
printf("%lld\n",(ans-1+mod)%mod);
}
return 0;
}