【题解】P4516 [JSOI2018] 潜入行动

P4516】题解

一:【题意】

  • 给定一棵树
  • 放置恰好k个监听器
  • 每个节点u仅当邻接点放置监听器,则u被监听
  • 求所有节点被监听的方案数

二:【解法】

  • 树上背包
  • 对于每个节点,我们只关注子树中有几个监听器,是否被监听,是否放置监听器
  • 所以我们设计dp[u][i][p][q]:u子树中选了i个点,u被监听的状态为p,u放置监听器的状态为q
  • 我们枚举dp[u][i][p][q]和dp[v][j][p'][q']更新dp[u][i+j][p|q'][q]
  • 树上背包复杂度O(nm)

三:【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;;
const int N=1e5+10,M=110,mod=1e9+7;
vector<int> mp[N];
int dp[N][M][2][2];int n,K;
int t[M][2][2];
int siz[N];
void Dp(int u,int fa){
	siz[u]=1;
	for(auto v:mp[u]){
		if(v==fa) continue;
		Dp(v,u);
	}
	dp[u][0][0][0]=1;dp[u][1][0][1]=1;
	
	for(auto v:mp[u]){
		if(v==fa) continue;
		int up=min(siz[u]+siz[v],K);
		for(int i=0;i<=min(K,siz[u]);i++)
			for(int j=0;j<=siz[v]&&i+j<=up;j++)
				for(int p=0;p<=1;p++)
					for(int q=0;q<=1;q++)
						for(int p1=0;p1<=1;p1++)
							for(int q1=0;q1<=1;q1++){
								if(q+p1==0) continue;
								t[i+j][p|q1][q]+=1ll*dp[u][i][p][q]*dp[v][j][p1][q1]%mod;
								t[i+j][p|q1][q]%=mod;
								//cout<<u<<" "<<i+j<<" "<<(p|q1)<<" "<<q<<" "<<dp[u][i+j][p|q1][q]<<"\n";
							}
		for(int i=0;i<=up;i++)
			for(int p=0;p<=1;p++)
				for(int q=0;q<=1;q++){
					dp[u][i][p][q]=t[i][p][q];
					t[i][p][q]=0;
				}
		siz[u]=up;
	}
}
int main(){
	cin>>n>>K;
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		mp[a].push_back(b);
		mp[b].push_back(a);
	}
	Dp(1,1);
	LL ans=1ll*dp[1][K][1][0]+dp[1][K][1][1];
	ans%=mod;
	cout<<ans<<"\n";
	return 0;
}
//dp[u][i][p][q]:u选了i点,u被监听为p,u放监听器为q
//dp[u][i+j][p|q'][q]
//if q+p'!=0 dp[u][i][p][q]*dp[v][j][p'][q']
posted @ 2025-12-17 09:12  Ming3398  阅读(11)  评论(0)    收藏  举报