label

本文介绍了一种利用对称轴特性的动态规划算法解决特定问题的方法。通过观察DP数组的对称性和中间部分的相等特性,文章提出了优化的DP实现方案,并给出了详细的C++代码实现。

题目描述

这里写图片描述

DP

很容易想到朴素DP
容易观察到DP数组有对称轴,而且中间一大段都是相等的,这个也易证。
于是就可以DP了。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100+10,maxm=10000+10,mo=1000000007;
int f[maxn][maxm],sum[maxn],num[maxm*2];
int h[maxn],go[maxn*2],next[maxn*2];
int i,j,k,l,r,t,n,m,tot,ca;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void dg(int x,int y){
    int t=h[x],i;
    fo(i,1,m) f[x][i]=0;
    if (go[t]==y&&!next[t]){
        fo(i,1,m) f[x][i]=1;
    }
    while (t){
        if (go[t]!=y) dg(go[t],x);
        t=next[t];
    }
    fo(i,1,m) num[i]=(num[i-1]+f[x][i])%mo;
    sum[x]=num[m];
    if (y){
        fo(i,1,m){
            t=(sum[x]-(num[min(m,i+k-1)]-num[max(i-k+1,1)-1])%mo)%mo;
            if (!f[y][i]) f[y][i]=t;else f[y][i]=(ll)f[y][i]*t%mo;
        }
    }
}
void dfs(int x,int y){
    int t=h[x],i;
    fo(i,1,n*k) f[x][i]=0;
    if (go[t]==y&&!next[t]){
        fo(i,1,n*k) f[x][i]=1;
    }
    while (t){
        if (go[t]!=y) dfs(go[t],x);
        t=next[t];
    }
    fo(i,1,n*k) num[i]=(num[i-1]+f[x][i])%mo;
    fo(i,n*k+1,n*k+k-1) num[i]=(num[i-1]+f[x][n*k])%mo;
    sum[x]=num[n*k]*2%mo;
    (sum[x]+=(ll)f[x][n*k]*((m-2*n*k)%mo)%mo)%=mo;
    if (y){
        fo(i,1,n*k){
            t=(sum[x]-(num[i+k-1]-num[max(i-k+1,1)-1])%mo)%mo;
            if (!f[y][i]) f[y][i]=t;else f[y][i]=(ll)f[y][i]*t%mo;
        }
    }
}
int main(){
    freopen("label.in","r",stdin);freopen("label.out","w",stdout);
    scanf("%d",&ca);
    while (ca--){
        scanf("%d%d%d",&n,&m,&k);
        /*fo(i,1,n)
            fo(j,1,min(m,n*k))
                f[i][j]=0;*/
        fo(i,1,n) sum[i]=h[i]=0;
        tot=0;
        fo(i,1,n-1){
            scanf("%d%d",&j,&l);
            add(j,l);add(l,j);
        }
        if (!k){
            t=1;
            fo(i,1,n) t=(ll)t*(m%mo)%mo;
            printf("%d\n",t);
            continue;
        }
        else if (m<=2*n*k) dg(1,0);else dfs(1,0);
        (sum[1]+=mo)%=mo;
        printf("%d\n",sum[1]);
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值