[JSOI2018]潜入行动(洛谷p4516)题解

传送门

一道树上背包简单题

状态很好推。设 d p ( x , i , 0 / 1 , 0 / 1 ) dp(x,i,0/1,0/1) dp(x,i,0/1,0/1)表示以 x x x为根的子树中共放了 i i i个监.听装置,其中 x x x点放没放装置, x x x点有没有被监.听到的方案数(在以 x x x为根的子树中除 x x x外的其它结点都被监.听到了)

不难看出这是一个树上背包,树上背包的转移套路是

d p ( x , i + j ) = c o m b i n e ( d p ( x , i ) , d p ( v , j ) ) dp(x,i+j)=combine(dp(x,i),dp(v,j)) dp(x,i+j)=combine(dp(x,i),dp(v,j)),其中 v v v x x x的子节点

所以本题的转移也就这样来考虑。把树画成这样,分为 x x x侧和 v v v
在这里插入图片描述
如果您有能力请自行推出方程,跳过这一段

  • 如果 x x x没被监.听,那么 v v v一定不能放装置,因此
    d p ( x , i + j , 0 , 0 ) = ∑ d p ( x , i , 0 , 0 ) × d p ( v , j , 0 , 1 ) dp(x,i+j,0,0)=\sum dp(x,i,0,0)\times dp(v,j,0,1) dp(x,i+j,0,0)=dp(x,i,0,0)×dp(v,j,0,1)
  • 如果 x x x没被监.听但是放了装置, x x x侧的状态一定是 d p ( x , i , 1 , 0 ) dp(x,i,1,0) dp(x,i,1,0) v v v是否被监.听无所谓但是一定不能放装置,因此
    d p ( x , i + j , 1 , 0 ) = ∑ d p ( x , i , 1 , 0 ) × ( d p ( v , j , 0 , 0 ) + d p ( v , j , 0 , 1 ) ) dp(x,i+j,1,0)=\sum dp(x,i,1,0)\times (dp(v,j,0,0)+dp(v,j,0,1)) dp(x,i+j,1,0)=dp(x,i,1,0)×(dp(v,j,0,0)+dp(v,j,0,1))
  • 如果 x x x没放装置但是被监.听了,这时候要分情况:
    x x x侧的状态是 d p ( x , i , 0 , 1 ) dp(x,i,0,1) dp(x,i,0,1),这时候反正 x x x已经被监.听了, v v v放不放装置都无所谓,但是必须保证 v v v是被监.听的,所以贡献是 d p ( x , i , 0 , 1 ) × ( d p ( v , j , 0 , 1 ) + d p ( v , j , 1 , 1 ) ) dp(x,i,0,1)\times (dp(v,j,0,1)+dp(v,j,1,1)) dp(x,i,0,1)×(dp(v,j,0,1)+dp(v,j,1,1))
    x x x侧的状态是 d p ( x , i , 0 , 0 ) dp(x,i,0,0) dp(x,i,0,0),这时候监.听 x x x的重任就要交给 v v v了,同时 v v v自己必须是被监.听的,所以贡献是 d p ( x , i , 0 , 0 ) × d p ( v , j , 1 , 1 ) dp(x,i,0,0)\times dp(v,j,1,1) dp(x,i,0,0)×dp(v,j,1,1)
    因此 d p ( x , i + j , 0 , 1 ) = ∑ ( d p ( x , i , 0 , 1 ) × ( d p ( v , j , 0 , 1 ) + d p ( v , j , 1 , 1 ) ) + d p ( x , i , 0 , 0 ) × d p ( v , j , 1 , 1 ) ) dp(x,i+j,0,1)=\sum (dp(x,i,0,1)\times (dp(v,j,0,1)+dp(v,j,1,1))+dp(x,i,0,0)\times dp(v,j,1,1)) dp(x,i+j,0,1)=(dp(x,i,0,1)×(dp(v,j,0,1)+dp(v,j,1,1))+dp(x,i,0,0)×dp(v,j,1,1))
  • 如果 x x x既放了装置又被监.听,同样要分两种情况:
    x x x侧的状态是 d p ( x , i , 1 , 0 ) dp(x,i,1,0) dp(x,i,1,0),需要让 v v v来监.听 x x x,但是 v v v是否被监.听无所谓,因为 x x x上放了装置可以保证 v v v被监.听,所以贡献是 d p ( x , i , 1 , 0 ) × ( d p ( v , j , 1 , 0 ) + d p ( v , j , 1 , 1 ) ) dp(x,i,1,0)\times (dp(v,j,1,0)+dp(v,j,1,1)) dp(x,i,1,0)×(dp(v,j,1,0)+dp(v,j,1,1))
    x x x侧的状态是 d p ( x , i , 1 , 1 ) dp(x,i,1,1) dp(x,i,1,1),这时候 x x x的所有要求都满足了, v v v怎么样都行,贡献是 d p ( x , i , 1 , 1 ) × ( d p ( v , j , 0 , 0 ) + d p ( v , j , 0 , 1 ) + d p ( v , j , 1 , 0 ) + d p ( v , j , 1 , 1 ) ) dp(x,i,1,1)\times (dp(v,j,0,0)+dp(v,j,0,1)+dp(v,j,1,0)+dp(v,j,1,1)) dp(x,i,1,1)×(dp(v,j,0,0)+dp(v,j,0,1)+dp(v,j,1,0)+dp(v,j,1,1))
    因此 d p ( x , i + j , 1 , 1 ) = ∑ ( d p ( x , i , 1 , 0 ) × ( d p ( v , j , 1 , 0 ) + d p ( v , j , 1 , 1 ) ) + d p ( x , i , 1 , 1 ) × ( d p ( v , j , 0 , 0 ) + d p ( v , j , 0 , 1 ) + d p ( v , j , 1 , 0 ) + d p ( v , j , 1 , 1 ) ) ) dp(x,i+j,1,1)=\sum (dp(x,i,1,0)\times (dp(v,j,1,0)+dp(v,j,1,1))+dp(x,i,1,1)\times (dp(v,j,0,0)+dp(v,j,0,1)+dp(v,j,1,0)+dp(v,j,1,1))) dp(x,i+j,1,1)=(dp(x,i,1,0)×(dp(v,j,1,0)+dp(v,j,1,1))+dp(x,i,1,1)×(dp(v,j,0,0)+dp(v,j,0,1)+dp(v,j,1,0)+dp(v,j,1,1)))

整理一下:

d p ( x , i + j , 0 , 0 ) = ∑ d p ( x , i , 0 , 0 ) × d p ( v , j , 0 , 1 ) dp(x,i+j,0,0)=\sum dp(x,i,0,0)\times dp(v,j,0,1) dp(x,i+j,0,0)=dp(x,i,0,0)×dp(v,j,0,1)

d p ( x , i + j , 1 , 0 ) = ∑ d p ( x , i , 1 , 0 ) × ( d p ( v , j , 0 , 0 ) + d p ( v , j , 0 , 1 ) ) dp(x,i+j,1,0)=\sum dp(x,i,1,0)\times (dp(v,j,0,0)+dp(v,j,0,1)) dp(x,i+j,1,0)=dp(x,i,1,0)×(dp(v,j,0,0)+dp(v,j,0,1))

d p ( x , i + j , 0 , 1 ) = ∑ ( d p ( x , i , 0 , 1 ) × ( d p ( v , j , 0 , 1 ) + d p ( v , j , 1 , 1 ) ) + d p ( x , i , 0 , 0 ) × d p ( v , j , 1 , 1 ) ) dp(x,i+j,0,1)=\sum (dp(x,i,0,1)\times (dp(v,j,0,1)+dp(v,j,1,1))+dp(x,i,0,0)\times dp(v,j,1,1)) dp(x,i+j,0,1)=(dp(x,i,0,1)×(dp(v,j,0,1)+dp(v,j,1,1))+dp(x,i,0,0)×dp(v,j,1,1))

d p ( x , i + j , 1 , 1 ) = ∑ ( d p ( x , i , 1 , 0 ) × ( d p ( v , j , 1 , 0 ) + d p ( v , j , 1 , 1 ) ) + d p ( x , i , 1 , 1 ) × ( d p ( v , j , 0 , 0 ) + d p ( v , j , 0 , 1 ) + d p ( v , j , 1 , 0 ) + d p ( v , j , 1 , 1 ) ) ) dp(x,i+j,1,1)=\sum (dp(x,i,1,0)\times (dp(v,j,1,0)+dp(v,j,1,1))+dp(x,i,1,1)\times (dp(v,j,0,0)+dp(v,j,0,1)+dp(v,j,1,0)+dp(v,j,1,1))) dp(x,i+j,1,1)=(dp(x,i,1,0)×(dp(v,j,1,0)+dp(v,j,1,1))+dp(x,i,1,1)×(dp(v,j,0,0)+dp(v,j,0,1)+dp(v,j,1,0)+dp(v,j,1,1)))

不是很长对吧

小心:这题dp数组开long long是会MLE的,要中间运算过程中转long long然后再转回int

就差不多了

#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>
#include <vector>

template <typename T> inline void read(T& t) {
    int f = 0, c = getchar(); t = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) t = t * 10 + c - 48, c = getchar();
    if (f) t = -t;
}
template <typename T> inline bool chkMin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkMax(T& x, const T& y) { return x < y ? (x = y, true) : false; }
#ifdef WIN32
#define LLIO "%I64d"
#else
#define LLIO "%lld"
#endif	// WIN32 long long
#define rep(I, A, B) for (int I = (A); I <= (B); ++I)
#define dwn(I, A, B) for (int I = (A); I >= (B); --I)
#define erp(I, X) for (int I = head[X]; I; I = next[I])

const int maxn = 1e5 + 7;
const long long mod = 1e9 + 7;
std::vector<int> G[maxn];
int dp[maxn][107][2][2], tmp[107][2][2];
int size[maxn];
int n, K;

inline int add(int x, long long y) {
    if (y >= mod) y %= mod;
    for (x += y; x >= mod; x -= mod);
    return x;
}
inline void ae(int x, int y) {
    G[x].push_back(y);
    G[y].push_back(x);
}
void dfs(int x, int fa) {
    size[x] = dp[x][0][0][0] = dp[x][1][1][0] = 1;
    for (unsigned e = 0; e < G[x].size(); ++e) {
        int v = G[x][e];
        if (v != fa) {
            dfs(v, x);
            rep(i, 0, std::min(size[x], K)) {
                tmp[i][0][0] = dp[x][i][0][0]; dp[x][i][0][0] = 0;
                tmp[i][0][1] = dp[x][i][0][1]; dp[x][i][0][1] = 0;
                tmp[i][1][0] = dp[x][i][1][0]; dp[x][i][1][0] = 0;
                tmp[i][1][1] = dp[x][i][1][1]; dp[x][i][1][1] = 0;
            }
            rep(i, 0, std::min(size[x], K))
                rep(j, 0, std::min(size[v], K - i)) {
                    dp[x][i + j][0][0] = add(dp[x][i + j][0][0], 1ll * tmp[i][0][0] * dp[v][j][0][1]);

                    dp[x][i + j][0][1] = add(dp[x][i + j][0][1], 1ll * tmp[i][0][1] * (dp[v][j][0][1] + dp[v][j][1][1]));
                    dp[x][i + j][0][1] = add(dp[x][i + j][0][1], 1ll * tmp[i][0][0] * dp[v][j][1][1]);

                    dp[x][i + j][1][0] = add(dp[x][i + j][1][0], 1ll * tmp[i][1][0] * (dp[v][j][0][0] + dp[v][j][0][1]));

                    dp[x][i + j][1][1] = add(dp[x][i + j][1][1], 1ll * tmp[i][1][0] * (dp[v][j][1][0] + dp[v][j][1][1]));
                    dp[x][i + j][1][1] = add(dp[x][i + j][1][1], 1ll * tmp[i][1][1] * (1ll * dp[v][j][0][0] + dp[v][j][0][1] + 1ll * dp[v][j][1][0] + dp[v][j][1][1]));
                }
                size[x] += size[v];
        }
    }
}

int main() {
    read(n); read(K);
    rep(i, 1, n - 1) {
        int x, y;
        read(x); read(y); ae(x, y);
    }
    dfs(1, 0);
    printf("%d\n", (int)((dp[1][K][0][1] + dp[1][K][1][1]) % mod));
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值