https://codeforces.com/contest/1276/problem/D
有点atcoder题的意思。
我们肯定是不能按时间顺序考虑的。
那么按树形dp的顺序考虑。
考虑以(i)为根的一棵子树里的边的情况,当然可能要加上(i->fa[i])这条边。
因为(i)的子节点的子树都做了,所以只用考虑(i->fa[i])这条边。
可能(fa[i])的其它边在(i->fa[i])之前,且把(fa[i])选了,影响到了(i->fa[i])。
可能(i->son[i])的边在(i->fa[i])之前,且把(i)选了,影响到了(i->fa[i])。
可能(i->fa[i])在(i->son[i])之前,把(i)选了,影响到了(i->son[i])。
设(f[i][0/1][0/1]),(i)为根子树内,第一个(0/1)表示(i->fa[i])之前的连着(fa[i])的边有没有选(fa[i]),第二个(0/1)表示出来之后(fa[i])选了没有。
转移十分简单。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;
const int mo = 998244353;
const int N = 2e5 + 5;
int n, x, y;
int fi[N], to[N * 2], nt[N * 2], tot = 1;
void link(int x, int y) {
nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
}
int fa[N], fq[N];
int d[N], d0;
ll f[N][2][2], g[2][2], h[2][2];
void dg(int x) {
for(int i = fi[x]; i; i = nt[i]) if(to[i] != fa[x]) {
int y = to[i];
fa[y] = x; fq[y] = i;
dg(y);
}
d0 = 0;
for(int i = fi[x]; i; i = nt[i])
d[++ d0] = to[i];
reverse(d + 1, d + d0 + 1);
fo(st, 0, 1) {
memset(g, 0, sizeof g);
g[0][st] = 1;
fo(i, 1, d0) {
memcpy(h, g, sizeof g);
memset(g, 0, sizeof g);
int y = d[i];
if(y == fa[x]) {
fo(u, 0, 1) fo(v, 0, 1) if(h[u][v]) {
if(!u && !v) {
g[1][0] = (g[1][0] + h[u][v]) % mo;
g[0][1] = (g[0][1] + h[u][v]) % mo;
} else {
g[u][v] = (g[u][v] + h[u][v]) % mo;
}
}
} else {
fo(u, 0, 1) fo(v, 0, 1) if(h[u][v]) {
fo(p, 0, 1) if(f[y][u][p])
g[p][v] = (g[p][v] + h[u][v] * f[y][u][p]) % mo;
}
}
}
f[x][st][0] = (g[0][0] + g[1][0]) % mo;
f[x][st][1] = (g[0][1] + g[1][1]) % mo;
}
}
int main() {
scanf("%d", &n);
fo(i, 1, n - 1) {
scanf("%d %d", &x, &y);
link(x, y); link(y, x);
}
dg(1);
pp("%lld
", f[1][1][1]);
}