树形dp进阶题2 JSOI2018潜入行动

JSOI2018 潜入行动

题目描述

外星人又双叒叕要攻打地球了,外星母舰已经向地球航行!这一次,JYY 已经联系好了黄金舰队,打算联合所有 JSOIer 抵御外星人的进攻。

在黄金舰队就位之前,JYY 打算事先了解外星人的进攻计划。现在,携带了监听设备的特工已经秘密潜入了外星人的母舰,准备对外星人的通信实施监听。

外星人的母舰可以看成是一棵 nn 个节点、 n-1n−1 条边的无向树,树上的节点用 1,2,\cdots,n1,2,⋯,n 编号。JYY 的特工已经装备了隐形模块,可以在外星人母舰中不受限制地活动,可以神不知鬼不觉地在节点上安装监听设备。

如果在节点 uu 上安装监听设备,则 JYY 能够监听与 uu 直接相邻所有的节点的通信。换言之,如果在节点 uu 安装监听设备,则对于树中每一条边 (u,v)(u,v) ,节点 vv 都会被监听。特别注意放置在节点 uu 的监听设备并不监听 uu 本身的通信,这是 JYY 特别为了防止外星人察觉部署的战术。

JYY 的特工一共携带了 kk 个监听设备,现在 JYY 想知道,有多少种不同的放置监听设备的方法,能够使得母舰上所有节点的通信都被监听?为了避免浪费,每个节点至多只能安装一个监听设备,且监听设备必须被用完。

输入输出格式

输入格式:
输入第一行包含两个整数 n,kn,k ,表示母舰节点的数量 nn 和监听设备的数量 kk 。 接下来 n-1n−1 行,每行两个整数 u,vu,v (1\le u,v\le n)(1≤u,v≤n) ,表示树中的一条边。

输出格式:
输出一行,表示满足条件的方案数。因为答案可能很大,你只需要输出答案 \text{mod 1,000,000,007}mod 1,000,000,007 的余数即可。

题目传送门

我看见有帅哥说这道题是简单的树形dp 。。。有点沉默

设 f[i][j][0/1][0/1] f[i][j][0/1][0/1] 表示i的子树中已安装j个装置, i是/否安装,是/否被控制的方案数, 方程过长懒得解释,这道题难就难在要打的东西太多很容易出错

然后要注意,很容易爆空间和时间,数组开int,虽然dp过程中会爆int,但是数组要开int不然会爆,然后在dp的时候强制转long long。 这个地方我调试了好久

#include<bits/stdc++.h>
using namespace std;
//#define int long long
typedef long long ll;
const int mod=1e9+7;
int N,K,f[100010][105][2][2],size[100010],front[100010];long long g[105][2][2];
struct tEdge{
    int v,next;
    tEdge(int v=0,int next=0) : v(v) , next(next) {}
}e[200020];
void add(int u,int v)
{
    static int tcnt=0;
    e[++tcnt]=tEdge(v,front[u]);front[u]=tcnt;
}
int mo(int &a,ll b){
    return (a+((int)(b%mod)))%mod;
}
void dfs(int u,int fa){
    size[u]=1;
    f[u][0][0][0]=f[u][1][1][0]=1;
    for(int i=front[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
        for(int i=0;i<=min(size[u],K);++i){
          g[i][0][0]=f[u][i][0][0],f[u][i][0][0]=0;
          g[i][0][1]=f[u][i][0][1],f[u][i][0][1]=0;
          g[i][1][0]=f[u][i][1][0],f[u][i][1][0]=0;
          g[i][1][1]=f[u][i][1][1],f[u][i][1][1]=0;
        }
        for(int i=0;i<=min(size[u],K);++i)
            for(int j=0;j<=min(size[v],K-i);++j){
                f[u][i+j][0][0]=mo(f[u][i+j][0][0],(ll)g[i][0][0]*(ll)f[v][j][0][1]);
                f[u][i+j][1][0]=mo(f[u][i+j][1][0],(ll)g[i][1][0]*(ll)(f[v][j][0][0]+f[v][j][0][1]));
                f[u][i+j][0][1]=mo(f[u][i+j][0][1],(ll)g[i][0][0]*(ll)f[v][j][1][1]+(ll)g[i][0][1]*(ll)(f[v][j][0][1]+f[v][j][1][1]));
                f[u][i+j][1][1]=mo(f[u][i+j][1][1],(ll)g[i][1][0]*(ll)(f[v][j][1][1]+f[v][j][1][0])+(ll)g[i][1][1]*(ll)((ll)(f[v][j][0][0]+f[v][j][0][1])+(ll)(f[v][j][1][1]+f[v][j][1][0])));

            }
        size[u]+=size[v];
    }
}
main(){
    scanf("%d%d",&N,&K);
    for(int i=1;i<N;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,0);
    printf("%d\n",(f[1][K][1][1]+f[1][K][0][1])%mod);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值