大家都知道的。
这种有点二次项展开的题是经典套路了。
先设一下树形dp, fi,0/1 表示i这个点选还是不选,子树的贡献。
显然还要维护0-k次幂的和。
合并的时候直接用二次项逆展开。
注意当x,y都选的时候,会变,此时相当于给 fy,1 合并个全部是1的数组。
Code:
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 2e5 + 5;
const int mo = 998244353;
int n, m, k, x, y, bz[N];
int final[N], to[N], next[N], tot;
ll f[N][2][11], c[11], g[11][11];
void link(int x, int y) {
next[++ tot] = final[x], to[tot] = y, final[x] = tot;
next[++ tot] = final[y], to[tot] = x, final[y] = tot;
}
ll p[11], q[11];
void bin(ll *a, ll *b) {
fo(i, 0, k) c[i] = 0;
fo(i, 0, k) fo(j, 0, i)
c[i] = (c[i] + a[j] * b[i - j] % mo * g[i][j]) % mo;
fo(i, 0, k) a[i] = c[i];
}
void dg(int x) {
bz[x] = 1;
for(int i = final[x]; i; i = next[i]) {
if(bz[to[i]]) continue;
dg(to[i]);
}
f[x][0][0] = f[x][1][0] = 1;
for(int i = final[x]; i; i = next[i]) {
int y = to[i]; if(bz[y]) continue;
fo(j, 0, k) p[j] = (f[y][0][j] + f[y][1][j]) % mo;
bin(f[x][0], p);
fo(j, 0, k) p[j] = f[y][1][j];
bin(p, q);
fo(j, 0, k) p[j] = (p[j] + f[y][0][j]) % mo;
bin(f[x][1], p);
}
bz[x] = 0;
}
int main() {
freopen("subgraph.in", "r", stdin);
freopen("subgraph.out", "w", stdout);
fo(i, 0, 10) {
g[i][0] = 1;
fo(j, 1, i) g[i][j] = (g[i - 1][j - 1] + g[i - 1][j]) % mo;
}
scanf("%d %d %d", &n, &m, &k);
fo(i, 1, m) {
scanf("%d %d", &x, &y);
link(x, y);
}
fo(i, 0, k) q[i] = 1;
dg(1);
printf("%lld", (f[1][0][k] + f[1][1][k]) % mo);
}