Description
Analysis
首先,40分的dp很容易想,设f[i][j]表示给i的子树做完,给i赋j的方案数。
f[i][j]=∏v∈son(i)f[v][x](|x−j|≥k)
随便乱搞些东西就有60~80分了。
然后有一个很显然却容易忽视的性质:f[i]是对称的。
而且中间有一大段的值是一样的!
可以证明(思考一下),因为 k<=100,所以 dp 值一端最多只有 99*100 个不同的。
所以只需要记录这么多个DP值,可以快速的算出来。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define efo(i,v) for(int i=last[v];i;i=next[i])
using namespace std;
typedef long long ll;
const int N=110,M=10010,mo=1e9+7;
int n,m,LYD,k,tot,to[N*2],next[N*2],last[N];
ll f[N][M],sum[N][M];
void link(int u,int v)
{
to[++tot]=v,next[tot]=last[u],last[u]=tot;
}
ll s(int v,int x)
{
if(x<=LYD) return sum[v][min(x,LYD)];
if(x<=m-LYD) return (sum[v][LYD]+(x-LYD)*f[v][LYD+1]%mo)%mo;
return (sum[v][LYD]+f[v][LYD+1]*(m-2*LYD)%mo+sum[v][LYD]-sum[v][m-x]+mo)%mo;
}
void dfs(int v,int fr)
{
fo(i,1,LYD+1) f[v][i]=1;
efo(i,v)
{
int u=to[i];
if(u==fr) continue;
dfs(u,v);
fo(j,1,LYD+1)
{
ll t=0;
if(j-k>0) t=s(u,j-k);
if(j+k<=m) t=(t+s(u,m)-s(u,j+k-1)+mo)%mo;
if(!k) t=(t-f[u][j]+mo)%mo;
(f[v][j]*=t)%=mo;
}
}
fo(i,1,LYD+1) sum[v][i]=(sum[v][i-1]+f[v][i])%mo;
}
int main()
{
freopen("label.in","r",stdin);
freopen("label.out","w",stdout);
int _,u,v;
for(scanf("%d",&_);_--;)
{
memset(f,0,sizeof(f));
memset(sum,0,sizeof(sum));
tot=0;
memset(last,0,sizeof(last));
scanf("%d %d %d",&n,&m,&k);
fo(i,1,n-1)
{
scanf("%d %d",&u,&v);
link(u,v),link(v,u);
}
LYD=min(m,10000);
dfs(1,1);
printf("%lld\n",s(1,m));
}
return 0;
}