JSOI2018 潜入行动

好久没有写单道题的题解了。之所以想写一写这道题,是因为它差不多是树形dp的模板的有一定思维难度的题了,有一定的纪念价值。

那么先看题目。

 

 

 

数据规模:

思路:既然题目给出了显式树,那么这道题很显然是道树形dp。既然是树形dp,我们就来列一下状态:

设f[u][j][k][l]表示以u节点为根的子树,安装j个监听器,且u节点放置监听器状态为k(0/1),被子节点监听状态为l(0/1),且除u外整棵子树均被监听。

那么就是一个树包,分别讨论f[u][j][0][0],f[u][j][0][1],f[u][j][1][0],f[u][j][1][1]的转移即可。

对于u的一个子节点v,分配t个监听器,于是有以下转移:

情况一:u节点既没有被监视,又没有放置装置,那么它的子节点不能放置装置,且必须被监视,f[u][j][0][0] += f[u][j - t][0][0] * f[v][t][0][1]

情况二:u节点没有被监视,但安放了装置,那么它的子节点不能放置装置,但是否被监视均可,f[u][j][1][0] += f[u][j - t][1][0] * (f[v][t][0][0] + f[v][t][0][1])

接下来两种情况比较复杂,

情况三:u节点被监视(父节点或子节点),但没有安放装置,

又分两种情况:

1.若u节点被子节点监视,则子节点必须安放装置,且必须被监视,f[u][j][0][1] += f[u][j - t][0][0] * f[v][t][1][1]

2.若u节点被父节点监视,则子节点是否安放装置均可,但必须被监视,f[u][j][0][1] += f[u][j - t][0][1] * (f[v][t][0][1] + f[v][t][1][1])

情况四:u节点被监视(父节点或子节点),且安放了装置

分两种情况:

1.若u节点被子节点监视,则子节点必须安放装置,是否被监视均可,f[u][j][1][1] += f[u][j - t][1][0] * (f[v][t][1][0] + f[v][t][1][1])

2.若u节点被父节点监视,则子节点无论是否安放装置,是否被监视均可,f[u][j][1][1] += f[u][j - t][1][1] * (f[v][t][0][0] + f[v][t][0][1] + f[v][t][1][0] + f[v][t][1][1])

以上就是所有转移了。

代码如下(转移采用刷表法,类似):

#include <bits/stdc++.h>

using namespace std;

const int mod = 1e9 + 7;

int f[100005][105][2][2], g[105][2][2];

struct node {
    int to, nxt;
}e[200005]; int head[100005], cnt[100005], tot, n, k;
inline void add_e(int u, int v) {e[++tot].to = v; e[tot].nxt = head[u]; head[u] = tot;}

void dp(int u, int fa)
{
    f[u][0][0][0] = f[u][1][1][0] = 1;
    cnt[u] = 1;
    for (int i = head[u]; i; i = e[i].nxt)
    {
        int v = e[i].to;
        if (v == fa) continue;
        dp(v, u);
        for (int j = 0; j <= min(cnt[u], k); j++) memcpy(g[j], f[u][j], sizeof f[u][j]), memset(f[u][j], 0, sizeof f[u][j]);
        for (int j = min(cnt[u], k); ~j; j--)
        {
            for (int t = 0; t <= min(cnt[v], k - j); t++)
            {
                f[u][j + t][0][0] = (f[u][j + t][0][0] + (1ll * g[j][0][0] * f[v][t][0][1] % mod)) % mod;
                f[u][j + t][1][0] = (f[u][j + t][1][0] + (1ll * g[j][1][0] * ((f[v][t][0][0] + f[v][t][0][1]) % mod)) % mod) % mod;
                f[u][j + t][0][1] = (f[u][j + t][0][1] + (1ll * g[j][0][1] * ((f[v][t][0][1] + f[v][t][1][1]) % mod)) % mod) % mod;
                f[u][j + t][0][1] = (f[u][j + t][0][1] + (1ll * g[j][0][0] * f[v][t][1][1]) % mod) % mod;
                f[u][j + t][1][1] = (f[u][j + t][1][1] + (1ll * g[j][1][0] * ((f[v][t][1][0] + f[v][t][1][1]) % mod)) % mod) % mod;
                f[u][j + t][1][1] = (f[u][j + t][1][1] + (1ll * g[j][1][1] * (((f[v][t][0][0] + f[v][t][0][1]) % mod + (f[v][t][1][0] + f[v][t][1][1]) % mod) % mod)) % mod) % mod;
            }
        } cnt[u] += cnt[v];
    }
}

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1, x, y; i < n; i++) 
    {
        scanf("%d%d", &x, &y);
        add_e(x, y);
        add_e(y, x);
    }
    dp(1, 0);
    printf("%d", (f[1][k][0][1] + f[1][k][1][1]) % mod);
}

 

posted @ 2021-03-23 19:18  Chasing-Dreams  阅读(40)  评论(0)    收藏  举报