P12844 [蓝桥杯 2025 国 A] 树 题解
我们首先根据题目中所说的“使得选择的任意两点之间的距离均大于
首先最直白的,选了自己就不能选儿子和孙子了。
然后就是,如果不选自己要选儿子的话,最多只能选一个儿子。选了两个的话,它们之间的距离就是
以及,如果不选自己,儿子能不能选是不确定的(因为这得看选没选父亲),但是孙子一定是不受影响的。
考虑树形 DP。定义
但是根据上面几条限制,我们会发现,选不选儿子节点对该节点的决策非常重要。那现在的状态定义就不够全面了。
扩展它。定义
那最终答案就是
于是接下来考虑转移。
-
- 由于你自己和儿子都不选,那么选不选孙子完全就是无所谓的。 - 于是有转移式 $dp_{u,0} = \prod_{v \in son(u)} (dp_{v,0} + dp_{v,2})$。 -
- 选了自己,孙子也受到牵连,和儿子一样都不能选。 - 所以有转移式 $dp_{u,1} = \prod_{v \in son(u)} dp_{v,0}$。 -
- 这算是最复杂的一种情况了。 - 由于选了儿子 $v$,那么其他的儿子 $w$ 都是不能选的,并且 $v$ 的儿子会受到限制,但是 $w$ 的儿子是可以随便选的(距离是 $3$ 不会炸)。 - 因此这里的转移式就稍微有些复杂,$dp_{u,2} = \sum_{v \in son(u)} dp_{v,1} \times \prod_{w \in son(u) \space \text{and} \space w \not= v} (dp_{w,0} \times dp_{w,2})$。
转移式都得到了。
爆了!怎么办呢?我们想要能快速求出转移式中后面
这样你会遍历你的儿子两遍,一遍往下递归并求上面提到的那个总乘积,还有一遍分别进行转移。
一定一定一定要时刻注意取模,不然炸了就老实了。
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
using namespace std;
const int N = 3e5+5;
const LL MOD = 998244353;
LL n,dp[N][3];
vector<int> g[N];
LL read(){
LL su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
LL QP(LL x,LL y){
LL as=1;
while(y){
if(y&1)as=as*x%MOD;
x=x*x%MOD,y>>=1;
}return as;
}
void DFS(int u,int fa){
LL res=1;dp[u][0]=1,dp[u][1]=1;
for(int v:g[u])if(v^fa)
DFS(v,u),res=res*(dp[v][0]+dp[v][2])%MOD;
for(int v:g[u])if(v^fa)
dp[u][0]=dp[u][0]*(dp[v][0]+dp[v][2])%MOD,
dp[u][1]=dp[u][1]*dp[v][0]%MOD,
dp[u][2]+=(dp[v][1]*(res*QP(dp[v][0]+dp[v][2],MOD-2)%MOD))%MOD,
dp[u][2]%=MOD;
return;
}
int main(){
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
g[x].pb(y),g[y].pb(x);
}DFS(1,0);
cout<<(dp[1][0]+dp[1][1]+dp[1][2]-1)%MOD;
return 0;
}
哎呀,忘了个事。你是不是发现我最后代码那里的输出多了个
如果本篇题解对你有帮助的话,麻烦你点一个小小的赞,真是太感谢啦!