[JSOI2018] 潜入行动 题解

T6 [JSOI2018] 潜入行动

很套路、很裸的一道树形 DP。看了状态就会推方程的那种。

\(f_{u,i,0/1,0/1}\) 表示以 \(u\) 为根的子树中有 \(i\) 个监听器、\(u\) 有没有监听器、\(u\) 有没有被监听的方案数。

显然要枚举子节点 \(v\)\(u\) 的监听器数量 \(i\)\(v\) 的监听器数量 \(j\)。用 \(f_{u,i}\)\(f_{v,j}\) 推出 \(f_{u,i+j}\)

然后挨个推一下就完了。

\(u\) 不放,也没有被监听,则 \(v\) 必定不放,但根据题意 \(v\) 之前需要被监听。故:

\[f_{u,i+j,0,0}=\sum f_{u,i,0,0}\times f_{v,j,0,1} \]

\(u\) 放了,但没有被监听,则 \(v\) 必定不放,且 \(v\) 之前是否被监听无所谓。故:

\[f_{u,i+j,1,0}=\sum f_{u,i,1,0}\times(f_{v,j,0,0}+f_{v,j,0,1}) \]

\(u\) 不放,但被监听了,此时分两种情况:要么 \(u\) 原本就被监听了,则 \(v\) 放不放无所谓,但因为 \(u\) 没放所以 \(v\) 之前需要被监听;要么 \(u\) 原本没有被监听,则需要 \(v\) 来监听 \(u\),也就是 \(v\) 必须放,同时因为 \(u\) 没放所以 \(v\) 之前需要被监听。故:

\[f_{u,i+j,0,1}=\sum\big(f_{u,i,0,1}\times(f_{v,j,0,1}+f_{v,j,1,1})+f_{u,i,0,0}\times f_{v,j,1,1}\big) \]

\(u\) 放了,也被监听了,此时分两种情况:要么 \(u\) 原本没有被监听,则 \(v\) 必须放来监听 \(u\),而 \(v\) 原本是否被监听无所谓;要么 \(u\) 原本就被监听了,此时 \(u\) 的所有限制都被满足,则 \(v\) 的情况也是不限的。故:

\[f_{u,i+j,1,1}=\sum\big(f_{u,i,1,0}\times(f_{v,j,1,0}+f_{v,j,1,1})+f_{u,i,1,1}\times(f_{v,j,0,0}+f_{v,j,1,0}+f_{v,j,0,1}+f_{v,j,1,1})\big) \]

就没了。

坑点:

  1. 空间复杂度是 \(O(4nk)\) 的,而本题贴心地给你开了 \(\texttt{256MB}\) 的空间,所以只能开 int 数组;
  2. 转移方程中出现的 \(f_{u,i}\) 需要开一个临时数组存储,否则会造成后效性影响转移;
  3. 转移结束后再写 siz[u]+=siz[v]
#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;

char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
void write(int x,char sf='\n'){
	if(x<0)putchar('-'),x=~x+1;
	int top=0;
	do str[top++]=x%10,x/=10;while(x);
	while(top)putchar(str[--top]+48);
	if(sf^'#')putchar(sf);
}
using ll=long long;
constexpr int MAXN=1e5+5,MOD=1e9+7;
int n,k,head[MAXN],tot;
struct{int v,to;}e[MAXN<<1];
void addedge(int u,int v){
	e[++tot]={v,head[u]};
	head[u]=tot;
}
void add(int&x,int y){
	x=x+y>=MOD?x+y-MOD:x+y;
}
int dp[MAXN][105][2][2],siz[MAXN],tmp[105][2][2];
void dfs(int u,int fno){
	siz[u]=dp[u][0][0][0]=dp[u][1][1][0]=1;
	for(int i=head[u],v=e[i].v;i;i=e[i].to,v=e[i].v){
		if(v==fno)continue;
		dfs(v,u);
		for(int i=0;i<=min(siz[u],k);++i){
			tmp[i][0][0]=dp[u][i][0][0],dp[u][i][0][0]=0;
			tmp[i][0][1]=dp[u][i][0][1],dp[u][i][0][1]=0;
			tmp[i][1][0]=dp[u][i][1][0],dp[u][i][1][0]=0;
			tmp[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){
				add(dp[u][i+j][0][0],(ll)tmp[i][0][0]*dp[v][j][0][1]%MOD);
				add(dp[u][i+j][1][0],(ll)tmp[i][1][0]*(((ll)dp[v][j][0][0]+dp[v][j][0][1])%MOD)%MOD);
				add(dp[u][i+j][0][1],((ll)tmp[i][0][1]*(((ll)dp[v][j][0][1]+dp[v][j][1][1])%MOD)%MOD+(ll)tmp[i][0][0]*dp[v][j][1][1]%MOD)%MOD);
				add(dp[u][i+j][1][1],((ll)tmp[i][1][0]*(((ll)dp[v][j][1][0]+dp[v][j][1][1])%MOD)%MOD+(ll)tmp[i][1][1]*(((ll)dp[v][j][0][0]+dp[v][j][0][1]+dp[v][j][1][0]+dp[v][j][1][1])%MOD)%MOD)%MOD);
			}
		siz[u]+=siz[v];
	}
}

int main(){
	n=read(),k=read();
	for(int i=1,u,v;i<n;++i){
		u=read(),v=read();
		addedge(u,v),addedge(v,u);
	}
	dfs(1,0);
	write((dp[1][k][0][1]+dp[1][k][1][1])%MOD);
	return fw,0;
}
posted @ 2024-10-15 22:05  Laoshan_PLUS  阅读(18)  评论(0编辑  收藏  举报