好?
首先我们可以发现,第一步和第三步的局面必须相等,因为第二步可以反着走回第一步,如果不相等那么下一步走的方案就不唯一了。
然后我们考虑走的形式,由于是一棵树,没有环,我们可以把走的过程拆分成一堆链,链的一段为空,其它的都有棋子,这样我们每次走相当于让一条链的所有点向空的那一段走。
再考虑什么样的链剖分是合法的:如果某条链的有棋子一段,它与另一条链接触,那么再走的时候接触的那条链可以不沿着链走而是走有棋子的那一端(有棋子的那段已经走开了),就导致走法不唯一了。同时,如果原来没棋子的一段,走过一次之后就变成有棋子了。
而如果两个链的两个端点相邻,且它们都有棋子,那么它们两个可以合并成一条链,方案也不唯一。同样的,如果两个都没棋子,走一次之后就都有棋子了。
那么我们可以总结出:一个合法的链剖分当且仅当每个链的两个端点只与异色端点相邻。
那么我们可以直接树形 DP:
我们将状态分为 5 类:
- \(u\) 节点非链尾,且所在链的链首有棋子;
- \(u\) 节点非链尾,且所在链的链首无棋子;
- \(u\) 节点为链尾,且无棋子;
- \(u\) 节点为链尾,且有棋子;
- \(u\) 节点是某个链中间的一个节点。
然后转移一下就行了。
自己做没想到如果原来没棋子,走一次之后有棋子,然后有些当前合法的状态就不合法了,有些不合法的情况也转移了,然后算出来答案巨大。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005, P = 998244353;
int n;
vector<int> e[MAXN];
long long f[MAXN][5];
void dfs(int u, int pre) {
long long sum2 = 1, sum3 = 1, sum4 = 1;
for (int v : e[u]) if (v != pre) {
dfs(v, u);
f[u][2] = (f[u][2] * f[v][3] + sum3 * f[v][0]) % P;
f[u][3] = (f[u][3] * f[v][2] + sum2 * f[v][1]) % P;
f[u][4] = (f[u][4] * f[v][4] + f[u][0] * f[v][1] + f[u][1] * f[v][0]) % P;
f[u][0] = (f[u][0] * f[v][4] + sum4 * f[v][0]) % P;
f[u][1] = (f[u][1] * f[v][4] + sum4 * f[v][1]) % P;
sum2 = sum2 * f[v][2] % P;
sum3 = sum3 * f[v][3] % P;
sum4 = sum4 * f[v][4] % P;
}
f[u][0] = (f[u][0] + sum2) % P;
f[u][1] = (f[u][1] + sum3) % P;
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
printf("%lld\n", (f[1][2] + f[1][3] + f[1][4]) % P);
return 0;
}
标签:sum4,Deterministic,ARC142D,int,Placing,sum3,sum2,棋子,节点
From: https://www.cnblogs.com/apjifengc/p/17058707.html