【gdoi2018 day2】滑稽子图

题目大意:

大家都知道的。

题解:

这种有点二次项展开的题是经典套路了。

先设一下树形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);
}

你可能感兴趣的:(树型dp,数论杂集)