【NOIP2016提高A组五校联考4】label

Description

给出一个n个节点的树,每个节点可以填[1,m]中的任意一个数。
相邻节点的数的绝对值必须>=k
求方案数
n,k<=100,m<=10^9

Solution

首先我们考虑如何解决一条链上的
如果不考虑m的范围,我们有一个显然的dp
太显然不写了。。。
观察这个方程式,因为第1位都可以填,所以都是1.
然后第2位,旁边的1~k-1,和m-k+2~m,因为范围越界了,所以答案会变小。
中间的都是一样的。。。
推广到第三位,因为第2位1~k都是不一样的,算上越界,1~2k都是不一样的。
然后这个数组是中心对称的。
所以我们只需要保留前(n-1)*k位来Dp
判断一下边界条件,分类讨论就好啦
(一点都不好)
考虑Dp上树,
Dp好像也是挺显然的~

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 105
#define M 10005
using namespace std;
typedef long long ll;
const int mo=1e9+7;
int n,m,k,ans,l,len,ty,x,y,f[N][M],sum[N][M];
int t[N*2],next[N*2],last[N];
void add(int x,int y) {
    t[++l]=y;next[l]=last[x];last[x]=l;
}
void dfs(int x,int y) {
    fo(i,1,len) f[x][i]=1;
    rep(i,x) if (t[i]!=y) {
        dfs(t[i],x);
        fo(j,1,len) {
            int tot=0;
            if (j-k>0) (tot+=sum[t[i]][j-k])%=mo;
            if (j+k+!k<=len) {
                (tot+=sum[t[i]][len]-sum[t[i]][j+k+!k-1])%=mo;
                (tot+=mo)%=mo;(tot+=sum[t[i]][len-1])%=mo;
                (tot+=(ll)f[t[i]][len]*(m-2*len+1)%mo)%=mo;
            } else if (j+k+!k<=m) {
                if (j+k+!k<=m-len+1) {
                    (tot+=sum[t[i]][len-1])%=mo;
                    (tot+=(ll)f[t[i]][len]*(m-len-j-k-!k+2)%mo)%=mo;
                } else (tot+=sum[t[i]][m-j-k-!k+1])%=mo;
            }
            f[x][j]=(ll)f[x][j]*tot%mo;
        }
    }
    fo(i,1,len) sum[x][i]=(sum[x][i-1]+f[x][i])%mo;
}
int mi(int x,int y) {
    int z=1;
    for(;y;y/=2,x=(ll)x*x%mo) if (y&1) z=(ll)z*x%mo;
    return z;
}
int main() {
    //freopen("label.in","r",stdin);
    //freopen("label.out","w",stdout);
    for(scanf("%d",&ty);ty;ty--) {
        scanf("%d%d%d",&n,&m,&k);ans=0;
        l=0;memset(last,0,sizeof(last));len=min(n*k+!k,m/2+m%2);
        fo(i,1,n-1) scanf("%d%d",&x,&y),add(x,y),add(y,x);
        dfs(1,0);fo(i,1,len-1) (ans+=f[1][i]*2%mo)%=mo;
        (ans+=(ll)f[1][len]*(m-2*len+2)%mo)%=mo;
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值