lgswdn_SA
2020-05-11 16:41:28
枚举每个点装不装监听器,然后判断是否可行
所以如果我在考场上可以拿
显然这是个树形背包,我们可以用树形背包的常见套路做。
(以下
对于
由于这个是乘法原理算种类数,所以应该是乘起来。
对于
那么可以转移到这个状态的
对于
对于
由于自己安装了,所
初始化:
推完式子就可以写代码了(转移方程长得有点难受)。
然后这题强制用 int 然后操作转 longlong 很杀人。
我对出题人卡空间的行为感到非常不满(
foin(x,y)
函数代表两数相加并取模(在 int 转 longlong 操作中很实用)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+3; const ll mod=1000000007;
struct edge{int to,nxt;}e[N*2]; int hd[N],tot;
void add(int u,int v){e[++tot]=(edge){v,hd[u]},hd[u]=tot;}
int n,k;
int foin(ll q,ll p){return (q+p)>mod?1ll*q+p-mod:q+p;}
int sz[N],f[N][103][2][2],t[103][2][2]; //树形背包常用的3个数组
void dfs(int u,int fa){
f[u][0][0][0]=f[u][1][1][0]=sz[u]=1;
for(register int p=hd[u],v;p;p=e[p].nxt)
if((v=e[p].to)!=fa){
dfs(v,u);
for(register int i=0;i<=min(sz[u],k);i++)
for(register int j=0;j<2;j++)
for(register int k=0;k<2;k++)
t[i][j][k]=f[u][i][j][k], f[u][i][j][k]=0;
for(register int i=0;i<=min(sz[u],k);i++){
for(register int j=0;j<=min(sz[v],k-i);j++){
f[u][i+j][0][0]=foin(f[u][i+j][0][0],1ll*t[i][0][0]*f[v][j][0][1]%mod);
f[u][i+j][0][1]=foin(f[u][i+j][0][1],(1ll*t[i][0][0]*f[v][j][1][1]+1ll*t[i][0][1]*(1ll*f[v][j][0][1]+f[v][j][1][1]))%mod);
f[u][i+j][1][0]=foin(f[u][i+j][1][0],1ll*t[i][1][0]*(1ll*f[v][j][0][0]+f[v][j][0][1])%mod);
f[u][i+j][1][1]=foin(f[u][i+j][1][1],foin((1ll*t[i][1][0]*(1ll*f[v][j][1][0]+f[v][j][1][1])%mod)
,1ll*t[i][1][1]*(1ll*f[v][j][0][0]+f[v][j][0][1]+f[v][j][1][0]+f[v][j][1][1])%mod));
}
}
sz[u]+=sz[v];
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1,u,v;i<n;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
dfs(1,0);
printf("%lld",1ll*(f[1][k][0][1]+f[1][k][1][1])%mod);
return 0;
}