[JSOI2018] 潜入行动

P4516 [JSOI2018] 潜入行动

考虑DP
dp[u][i][0/1][0/1]表示 以u为根的子树中共放了i个 ,是否放了监听装置,是否被监听到

1.dp[u][i][0][0]表示 以u为根的子树中共放了i个 没放监听装置 没被监听到 
2.dp[u][i][0][1]表示 以u为根的子树中共放了i个 没放监听装置 被监听到 
4.dp[u][i][1][0]表示 以u为根的子树中共放了i个 放了监听装置 没被监听到 
4.dp[u][i][1][1]表示 以u为根的子树中共放了i个 放了监听装置 被监听到 

用01代替 是否放了监听装置,是否被监听到

然后由于是求方案数,子树方案相乘,分类讨论之后相加
结果为 dp[1][k][0][1]和dp[1][k][1][1] 两种被监听到的方案之和

对于多棵子树的结果我们合并到根节点
1.如果u没被监听,没放装置,v有 0,0 和 0,1两种状态

dp[u][i+j][0][0] = ∑dp[x][i][0][0]*dp[v][j][0][1]

2.如果u没被监听,放了装置,v有 0,0 和 0,1两种状态

dp[u][i+j][1][0] = ∑dp[x][i][1][0]*(dp[v][j][0][0]+dp[v][j][0][1])

3.如果u被监听了,没放装置,v有 0,0 、 0,1 、 1,1 、1,0 四种状态

dp[u][i+j][0][1] = ∑(dp[u][i][0][1]*(dp[v][j][0][1]+dp[v][j][1][1])+dp[u][i][0]  [0]*dp[v][j][1][1])

4.如果u被监听了,放了装置,v有 0,0 、 0,1 、 1,1 、1,0 四种状态

dp[u][i+j][1][1] = ∑(dp[u][i][1][0]*(dp[v][j][1][0]+dp[v][j][1][1])+dp[u][i][1][1]*(dp[v][j][0][0]+dp[v][j][0][1]+dp[v][j][1][0]+dp[v][j][1][1]))

时间复杂度O(nk)

// P4516 [JSOI2018] 潜入行动 
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;

int read() {

	int x=0,f=1;
	char ch=getchar();
	while(ch<48||ch>57) {
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>=48&&ch<=57) {
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return x*f;

}

int head[N],to[N*2],nxt[N*2],tot=0;

void add(int u,int v) {

	nxt[++tot]=head[u];
	to[tot]=v;
	head[u]=tot;

}

int n,k,dp[N][101][2][2];
int g[N][101][2],siz[N];

int get_mod(ll x,ll y) {

	x%=mod;
	y%=mod;
	return (int)(x+y)%mod;

}

void dfs(int u,int fa) {

	siz[u]=dp[u][0][0][0]=dp[u][1][1][0]=1;
	for(int o=head[u]; o; o=nxt[o]) {

		int v=to[o];
		if(v==fa) continue;
		dfs(v,u);

		for(int i=0; i<=min(siz[u],k); i++) {

			g[i][0][0]=dp[u][i][0][0],dp[u][i][0][0]=0;
			g[i][0][1]=dp[u][i][0][1],dp[u][i][0][1]=0;
			g[i][1][0]=dp[u][i][1][0],dp[u][i][1][0]=0;
			g[i][1][1]=dp[u][i][1][1],dp[u][i][1][1]=0;

		}

		for(int i=0; i<=min(siz[u],k); i++) {

			for(int j=0; j<=min(siz[v],k-i); j++) {

				dp[u][i+j][0][0]=get_mod((ll)dp[u][i+j][0][0],(ll)g[i][0][0]*(ll)dp[v][j][0][1]);
				dp[u][i+j][0][1]=get_mod((ll)dp[u][i+j][0][1],(ll)g[i][0][0]*(ll)dp[v][j][1][1]+(ll)g[i][0][1]*((ll)dp[v][j][1][1]+(ll)dp[v][j][0][1]));
				dp[u][i+j][1][0]=get_mod((ll)dp[u][i+j][1][0],(ll)g[i][1][0]*((ll)dp[v][j][0][0]+(ll)dp[v][j][0][1]));
				dp[u][i+j][1][1]=get_mod((ll)dp[u][i+j][1][1],(ll)g[i][1][0]*((ll)dp[v][j][1][0]+(ll)dp[v][j][1][1])+(ll)g[i][1][1]*((ll)dp[v][j][0][0]+(ll)dp[v][j][0][1]+(ll)dp[v][j][1][0]+(ll)dp[v][j][1][1]));

			}

		}

		siz[u]+=siz[v];

	}


}

int main() {

	n=read();
	k=read();

	for(int i=1; i<n; i++) {

		int u=read(),v=read();
		add(u,v);
		add(v,u);

	}

	dfs(1,0);
	printf("%d\n",(int)(dp[1][k][0][1]+dp[1][k][1][1])%mod);

	return 0;
}
posted @   Diamondan  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示