题解:P12844 [蓝桥杯 2025 国 A] 树
HydrogenOxygen · · 题解
题解:P12844 [蓝桥杯 2025 国 A] 树
考虑树形 DP。
不想截图了,就凑合着看字符画吧。
如果根节点不选,就选成这样(o 表示不选,x 表示可以选也可以不选,.................. 表示以下都不考虑)
o
/|\
x o o
/ / \ \
o x o x
..................
o
/|\
x o o
/ / \ \
o o x x
..................
o
/|\
o x o
/ / \ \
x o o x
..................
o
/|\
o o x
/ / \ \
x x o o
..................
o
/|\
o o x
/ / \ \
x o x o
..................
可以看到,子节点至多选一个;不选的子节点的子节点也最多选一个;选的子节点就是其子树的根节点选上的结果。
如果根节点选,那么长这样:
x
/|\
o o o
/ / \ \
o o o o
..................
根节点选了,子节点及其子节点都不能选。
那么重新定义状态:
然后就有转移方程:
除法用逆元维护。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
const int maxn=3e5+5;
vector<int>G[maxn];
ll dp[maxn][3];// 0为自己选;剩下为自己不选:1为下面有一个选的;2为下面都不选
ll n;
const int MOD=998244353;
int qpow(ll x,int y=MOD-2){
ll ans=1;
while(y){
if(y&1)ans*=x,ans%=MOD;
x*=x;
x%=MOD;
y>>=1;
}
return ans;
}
void dfs(int u,int fa){
dp[u][0]=1;
dp[u][1]=0;
dp[u][2]=1;
for(auto v:G[u]){
if(v==fa)continue;
dfs(v,u);
dp[u][2]=dp[u][2]*(dp[v][1]+dp[v][2])%MOD;
dp[u][0]=dp[u][0]*dp[v][2]%MOD;
}
for(auto v:G[u]){
if(v==fa)continue;
dp[u][1]+=dp[u][2]*qpow(dp[v][1]+dp[v][2])%MOD*dp[v][0]%MOD;
dp[u][1]%=MOD;
}
}
signed main(){
cin>>n;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
cout<<((dp[1][0]+dp[1][1]+dp[1][2]-1)%MOD+MOD)%MOD<<"\n";
return 0;
}