[BZOJ5314][JSOI2018]潜入行动
有一说一,这题在洛谷上能评到黑题??
但是其实还可以了,锻炼细节能力和分析能力吧
题目大意:
有n个点,有k个监视器,每个点至多放一个,被放在点上的监视器可以监控到与他相连的所有点但不包括他自己,求覆盖到整棵树并且用完k个监视器的方案数
状态应该挺好想的:
设(dp[i][j][0/1][0/1])表示在根为(i)的子树中放了(j)个监视器,(i)是否放了,(i)是否被监视到,0表示没有。
然后是比较麻烦和细节的转移
注意:先把(dp[i])拷贝出来并清空,然后再dp
讲下转移的过程
对于树u,枚举儿子v,在v之前放了多少个监视器j,在v中放了多少个监视器t
则由(dp[u][j]×dp[v][t])就可以转移到(dp[u][j+t])
具体一些:
(f为拷贝出来的dp[u])
第一种情况:u上不放,u不被监视到
分析:v之前u必须也不放,不被监视到,v上不能放,但是要保证u的子树都要被监视到,所以v的第四维为1,根据乘法原理计数即可
第二种情况:u上放,u不被监视到
分析:在v之前u上放了,由于u上放了,所以会让v一定被监视到,所以(dp[v][t][0])的第四维取0/1均可(因为在这之后v始终会被u监视)
第三种情况:u上不放,u被监视到了
分析:由于u不放所以v必须已经被监视到
然后两种子情况:
1、在v之前u已经被监视,此时v上放不放监视器都不影响,所以都取
2、在v之前u未被监视到,由于v上放了监视器所以u被监视到了
都加起来就行
第四种情况:u上放了,u被监视到
分析:因为u上放了,所以v被不被监视到都可以取,因为v在这之后会被u监视到
依然是两种子情况:
1、在v之前u已经被监视到了:此时v上放不放监视器都行
(那就是v上放不放监视器,v被不被监视到都行,共4种
2、在v之前u尚未被监视到:此时v上必须放监视器
作为个强迫症,我把第一个dp中(f[j][1][1]×(dp[v][t][1][0]+dp[v][t][1][1]))的部分也放在下面来了(解释下代码和分析的不一样之处
最后输出答案的时候,要把(dp[1][k][0][1])和(dp[1][k][1][1])加起来,因为1节点放不放都行,只要监视到即可。
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define res(i,x,y) for (int i=x;i>=y;i--)
using namespace std;
typedef long long ll;
const int maxn=100010,mod=1000000007;
int n,k;
struct Edge{int v,nex;}edge[maxn<<1];
int cnt=0,head[maxn];
int dp[maxn][105][2][2];
int siz[maxn];
inline void addEdge(int u,int v){
edge[++cnt]=(Edge){v,head[u]};head[u]=cnt;
}
void add(int &x,int y){
x+=y;
if (x>mod)x-=mod;
}
int f[105][2][2];
void dfs(int u,int fa){
dp[u][0][0][0]=dp[u][1][1][0]=1;siz[u]=1;
//printf("u=%d
",u);
for (int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if (v==fa) continue;
dfs(v,u);
memcpy(f,dp[u],sizeof(f));
rep(j,0,k) rep(x1,0,1) rep(x2,0,1) dp[u][j][x1][x2]=0;
rep(j,0,min(siz[u],k))
rep(t,0,min(siz[v],k)){
if (j+t>k) continue;
add(dp[u][j+t][0][0],(ll)f[j][0][0]*dp[v][t][0][1]%mod);
add(dp[u][j+t][1][0],(ll)f[j][1][0]*(dp[v][t][0][1]+dp[v][t][0][0])%mod);
add(dp[u][j+t][0][1],(ll)f[j][0][1]*(dp[v][t][1][1]+dp[v][t][0][1])%mod);
add(dp[u][j+t][0][1],(ll)f[j][0][0]*dp[v][t][1][1]%mod);
add(dp[u][j+t][1][1],(ll)(f[j][1][0]+f[j][1][1])*(dp[v][t][1][0]+dp[v][t][1][1])%mod);
add(dp[u][j+t][1][1],(ll)f[j][1][1]*(dp[v][t][0][0]+dp[v][t][0][1])%mod);
}
siz[u]+=siz[v];
}
}
int read(){
int x=0;char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) x=x*10+ch-48,ch=getchar();
return x;
}
int main(){
n=read();k=read();
memset(dp,0,sizeof(dp));
rep(i,1,n-1){
int u=read(),v=read();
addEdge(u,v);addEdge(v,u);
}
dfs(1,0);
add(dp[1][k][0][1],dp[1][k][1][1]);
printf("%lld
",dp[1][k][0][1]);
return 0;
}