題目鏈接: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
*/