Codeforces 1276D. Tree Elimination(树形dp)
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("\n")
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\n", f[1][1][1]);
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址