题目链接:https://codeforc.es/contest/1332/problem/F
思路:这题选的是除了空集之外边的子集,一开始做了个联通子图的,自闭了好久。这题可以先考虑选的边是全集怎么做,边是全集很明显可以用dp[u][1]表示这个点染色了的独立集数目,dp[u][0]表示这个点没染色的独立集数目。转移方程也很显然,如果当前点染色了,那么它的儿子必须是没染色的,否则它的儿子可以是任何颜色。转移方程也很显然
对于这个题只要加上一个状态dp[u][2]表示u点和它每个儿子都断开的方案数就行了,用qw[v]表示父节点与v断开,v子树的所有方案数,对于dp[u][1]和dp[u][0]来说如果u-v这条边被删掉了,那么v这颗子树怎么选都是可以的,所以转移时需要加上qw[v]的情况,而对于dp[u][2]来说由于每个儿子都断开了,那么它的方案数就是儿子节点qw[v]的乘积,需要这个状态的原因是由于和儿子都断开后,这个点变成了一个独立的点,那么其实它怎么染色都无所谓,但dp[v][0]+dp[v][1]重复计算了这种情况,在计算qw[v]的时候需要把这种重复情况减去
需要注意的是最后的答案需要减去空集的情况
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
using namespace __gnu_pbds;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll inff = 0x3f3f3f3f3f3f3f3f;
#define FOR(i,a,b) for(int i(a);i<=(b);++i)
#define FOL(i,a,b) for(int i(a);i>=(b);--i)
#define REW(a,b) memset(a,b,sizeof(a))
#define inf int(0x3f3f3f3f)
#define si(a) scanf("%d",&a)
#define sl(a) scanf("%I64d",&a)
#define sd(a) scanf("%lf",&a)
#define ss(a) scanf("%s",a)
#define mod ll(998244353)
#define pb push_back
#define eps 1e-6
#define lc d<<1
#define rc d<<1|1
#define Pll pair<ll,ll>
#define P pair<int,int>
#define pi acos(-1)
#define VI vector<vector<int>>
using namespace std;
const int N=3e5+8;
ll n,x,y,dp[N][3],ans;
vector<int>g[N];
void dfs(int u,int fa)
{
dp[u][0]=dp[u][1]=dp[u][2]=1;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==fa) continue;
dfs(v,u);
ll qw=(dp[v][0]+dp[v][1]-dp[v][2]+mod)%mod;
dp[u][0]=dp[u][0]*(dp[v][0]+dp[v][1]+qw)%mod;
dp[u][1]=dp[u][1]*(dp[v][0]+qw)%mod;
dp[u][2]=dp[u][2]*qw%mod;
}
}
int main()
{
cin.tie(0);
cout.tie(0);
cin>>n;
FOR(i,1,n-1)
{
sl(x),sl(y);
g[x].pb(y);
g[y].pb(x);
}
dfs(1,0);
cout<<(dp[1][1]+dp[1][0]-dp[1][2]-1+mod)%mod<<endl;
return 0;
}
/*
1
5
3 5 1 2 4
3 3 2 3 1
*/