【JZOJ4816】label
Description
给出一棵树,给每个节点赋值一个 [1,m] 之间的权值,要求有边相连的两个点差绝对值 ≥k 。求方案数,答案模 109+7 。
Solution
20分:
Fi,j=∏v∈soni∑k=1mFv,k(|k−j|≥k)
40分:
我们发现可取
F
是连续的,维护一个前缀和即可。
100分:
找规律发现,
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<cmath>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 101
#define M 201
#define MAX 10200
#define mo 1000000007
#define ll long long
using namespace std;
int to[M],next[M],last[M],num=0;
int K;
int fa[N];
void link(int x,int y)
{
num++;
to[num]=y;
next[num]=last[x];
last[x]=num;
}
int n,m;
ll f[N][MAX],g[N][MAX],z[N][MAX];
int p[N];
int zz;
ll sum(int x,int y)
{
if(y<=p[x]) return g[x][y];
else if(y<=m-p[x]+1) return (g[x][p[x]]+f[x][p[x]+1]*(y-p[x])%mo)%mo;
else return (g[x][p[x]]+f[x][p[x]+1]*(m-2*p[x])%mo+z[x][y-m+p[x]])%mo;
}
void dfs(int x)
{
fo(i,1,zz) f[x][i]=1;
for(int i=last[x];i;i=next[i])
{
int v=to[i];
if(fa[x]!=v)
{
fa[v]=x;
dfs(v);
fo(j,1,zz)
{
ll tmp=0;
tmp=(tmp+sum(v,max(j-K,0)))%mo;
tmp=(tmp+sum(v,m)-sum(v,min(j+K,m+1)-1)+mo)%mo;
if(!K) tmp=(tmp-f[v][j]+mo)%mo;
f[x][j]=f[x][j]*tmp%mo;
}
}
}
fo(i,1,zz)
if(f[x][i]==f[x][i+1]) {p[x]=i;break;}
if(!p[x]) p[x]=zz;
fo(i,1,zz)
{
g[x][i]=(g[x][i-1]+f[x][i])%mo;
z[x][i]=(z[x][i-1]+f[x][p[x]-i+1])%mo;
}
}
int main()
{
freopen("label.in","r",stdin);
freopen("label.out","w",stdout);
int T;
cin>>T;
while(T--)
{
num=0;
memset(last,0,sizeof(last));
memset(p,0,sizeof(p));
scanf("%d %d %d",&n,&m,&K);
fo(i,1,n-1)
{
int x,y;
scanf("%d %d",&x,&y);
link(x,y);
link(y,x);
}
zz=min(m,max((n-1)*K,2));
dfs(1);
printf("%lld\n",sum(1,m));
}
}