潜入行动「JSOI2018」

题意

给定一颗无根树,可以在上面放置恰好\(k\)个监听器,与监听器相连的节点被监听,但监听器本身不会被监听。

要求监听所有节点,求方案数模1e9+7。


思路

状态略有毒瘤之处的树上dp。

子状态为\(dp[n][k][0/1][0/1]\)表示在以\(n\)为根的子树中选择了\(k\)个节点,其中是否选择\(n\)\(n\)是否被监听。

状态转移方程显然,就是极其容易写错。

本题卡\(long long\),注意使用\(unsigned int\)

代码

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

	streambuf*in=cin.rdbuf();
	char bb[1000000],*s=bb,*t=bb;
	#define gc() (s==t&&(t=(s=bb)+in->sgetn(bb,1000000),s==t)?EOF:*s++)
	template<typename T> inline void read(T &x){
		x=0;
		char ch=gc();
		while(ch<48)ch=gc();
		while(ch>=48)x=x*10+ch-48,ch=gc();
	}
	template<typename T> inline void write (T x) {
		if (x<0) putchar('-'),x=-x;
		if (x>=10) write(x/10);
		putchar(x%10+'0');
	}

}

using namespace StandardIO;

namespace Solve {
	
	const int N=100001;
	const int MOD=1000000007;
	
	int n,k,ans;
	int cnt;
	int head[N];
	struct node {
		int to,next;
	} edge[N<<1];
	unsigned int dp[N][101][2][2];
	long long cur[101][2][2],size[N];

	inline int min (const int &x,const int &y) {
		return (x>y)?y:x;
	}
	inline int max (const int &x,const int &y) {
		return (x<y)?y:x;
	}
	inline void add (int a,int b) {
		edge[++cnt].to=b,edge[cnt].next=head[a],head[a]=cnt;
	}
	void dfs (int now,int fa) {
		size[now]=1,dp[now][0][0][0]=dp[now][1][1][0]=1;
		for (register int i=head[now]; i; i=edge[i].next) {
			int to=edge[i].to;
			if (to==fa) continue;
			dfs(to,now);
			int limit1=min(k,size[now]),limit2=min(k,size[to]);
			for (register int j=0; j<=limit1; ++j) {
				cur[j][0][0]=dp[now][j][0][0],cur[j][0][1]=dp[now][j][0][1];
				cur[j][1][0]=dp[now][j][1][0],cur[j][1][1]=dp[now][j][1][1];
				dp[now][j][0][0]=dp[now][j][0][1]=dp[now][j][1][0]=dp[now][j][1][1]=0;
			}
			for (register int j=0; j<=limit1; ++j) {
				for (register int t=0; t<=limit2&&j+t<=k; ++t) {
					dp[now][j+t][0][0]+=cur[j][0][0]*dp[to][t][0][1]%MOD;
					dp[now][j+t][0][1]+=(cur[j][0][0]*dp[to][t][1][1]%MOD+cur[j][0][1]*(dp[to][t][0][1]+dp[to][t][1][1])%MOD)%MOD;
					dp[now][j+t][1][0]+=cur[j][1][0]*((dp[to][t][0][0]+dp[to][t][0][1])%MOD)%MOD;
					dp[now][j+t][1][1]+=(cur[j][1][0]*(dp[to][t][1][0]+dp[to][t][1][1])%MOD+cur[j][1][1]*(dp[to][t][0][0]+dp[to][t][1][0]+dp[to][t][0][1]+dp[to][t][1][1])%MOD)%MOD;
					dp[now][j+t][0][0]%=MOD,dp[now][j+t][0][1]%=MOD;
					dp[now][j+t][1][0]%=MOD,dp[now][j+t][1][1]%=MOD;
				}
			}
			size[now]+=size[to];
		}
	}
	
	inline void MAIN () {
		read(n),read(k);
		for (register int i=1; i<n; ++i) {
			int u,v;
			read(u),read(v);
			add(u,v),add(v,u);
		}
		dfs(1,1);
		ans=(dp[1][k][0][1]+dp[1][k][1][1])%MOD;
		write(ans);
	}
	
}

int main () {
	Solve::MAIN();
}
posted @ 2019-08-20 13:01  Ilverene  阅读(272)  评论(0编辑  收藏  举报