bzoj5314: [Jsoi2018]潜入行动【树形dp】

Description

外星人又双叒叕要攻打地球了,外星母舰已经向地球航行!这一次,JYY已经联系好了黄金舰队,打算联合所有JSO
Ier抵御外星人的进攻。在黄金舰队就位之前,JYY打算事先了解外星人的进攻计划。现在,携带了监听设备的特工
已经秘密潜入了外星人的母舰,准备对外星人的通信实施监听。外星人的母舰可以看成是一棵n个节点、n-1条边的
无向树,树上的节点用1,2…n编号。JYY的特工已经装备了隐形模块,可以在外星人母舰中不受限制地活动,可以
神不知鬼不觉地在节点上安装监听设备。如果在节点u安装监听设备,则JYY能够监听与u直接相邻所有的节点的通
信。换言之,如果在节点u安装监听设备,则对于树中每一条边(u,v),节点v都会被监听。特别注意放置在节点u的
监听设备并不监听u本身的通信,这是JYY特别为了防止外星人察觉部署的战术。
JYY的特工一共携带了k个监听设备,现在JYY想知道,有多少种不同的放置监听设备的方法,能够使得母舰上所有
节点的通信都被监听?为了避免浪费,每个节点至多只能安装一个监听设备,且监听设备必须被用完。

Input

输入第一行包含两个整数n,k,表示母舰节点的数量n和监听设备的数量k。
接下来n-1行,每行两个整数u,v(1≤u,v≤n),表示树中的一条边。
1≤n≤10^5,1≤k≤min{n,100}

Output

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

Sample Input

5 3

1 2

2 3

3 4

4 5

Sample Output

1

样例解释

样例数据是一条链

1–2–3–4–5

首先,节点 2和 4必须放置监听设备,否则 1,5将无法被监听(放置的监听设备无法监听它所在的节点)。剩下一

个设备必须放置在 3号节点以同时监听 2,4因此在 2,3,4节点放置监听设备是唯一合法的方案。

解题思路:

注意答案是恰好安k个的方案数。
f[i][j][0/1][0/1] f [ i ] [ j ] [ 0 / 1 ] [ 0 / 1 ] 表示 i i 的子树内安了 j 个监听器, i i <script type="math/tex" id="MathJax-Element-5">i</script> 处是否安了监听器,是否被监听,直接dp即可。

#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=100005,mod=1e9+7;
int n,K,f[N][105][2][2],size[N];ll tmp[105][2][2];
int tot,first[N],nxt[N<<1],to[N<<1];
inline void add(int &x,ll y)
{
    x=(x+y>=mod?x+y-mod:x+y);
}
void Add(int x,int y)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y;
}
void dfs(int u,int fa)
{
    size[u]=1;f[u][0][0][0]=f[u][1][1][0]=1;
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];if(v==fa)continue;
        dfs(v,u);
        for(int i=0,r=min(size[u],K);i<=r;i++)
            for(int j=0;j<=1;j++)
                for(int k=0;k<=1;k++)
                    tmp[i][j][k]=f[u][i][j][k],f[u][i][j][k]=0;
        for(int i=0,r1=min(size[u],K);i<=r1;i++)
            for(int j=0,r2=min(size[v],K);i+j<=K&&j<=r2;j++)
            {
                add(f[u][i+j][0][0],tmp[i][0][0]*f[v][j][0][1]%mod);
                add(f[u][i+j][0][1],(tmp[i][0][0]*f[v][j][1][1]+tmp[i][0][1]*(f[v][j][0][1]+f[v][j][1][1]))%mod);
                add(f[u][i+j][1][0],tmp[i][1][0]*(f[v][j][0][0]+f[v][j][0][1])%mod);
                add(f[u][i+j][1][1],(tmp[i][1][0]*(f[v][j][1][0]+f[v][j][1][1])+tmp[i][1][1]*(f[v][j][0][0]+f[v][j][1][0])+tmp[i][1][1]*(f[v][j][0][1]+f[v][j][1][1]))%mod);
            }
        size[u]+=size[v];
    }
}
int main()
{
    //freopen("action.in","r",stdin);
    //freopen("action.out","w",stdout);
    n=getint(),K=getint();
    for(int i=1;i<n;i++)
    {
        int x=getint(),y=getint();
        Add(x,y),Add(y,x);
    }
    dfs(1,0);
    cout<<(f[1][K][0][1]+f[1][K][1][1])%mod<<'\n';
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值