您的位置:首页 > 移动开发

HDU 4906 Our happy ending 状压DP

2017-07-26 18:34 447 查看
题意:给出n,k,L,问有多少种长度为n的序列,其0<=a[i]<=L 并且能从序列中选出某些数其和为k.

n,k<=20, 0<=L<=1e9.

某些和最多就20种,状态压一压,第i位二进制为1代表序列和可以为i,

设dp[i][s] 长度为i状态为s的方案数.

每次加入x(x<=min(k,l))时 状态中原本第i位为1,第i+x位也为1 超过k位就没必要考虑了.
大于k的数有cnt个 则长度+1,s->s转移到自己就有cnt种方案

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1<<21;
ll n,k,L,S;
ll dp[2]
;
ll calc(ll s,ll x)
{
ll res=s,t=s;
t=(t<<x)&S;
res|=(1ll<<(x-1));
res|=t;
return res;
}
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%lld%lld%lld",&n,&k,&L);
memset(dp,0,sizeof(dp));
dp[0][0]=1;
S=(1<<k)-1;
int now=1,pre=0;
ll cnt=1;//zero
if(L>k)
cnt+=L-k;
for(int i=1;i<=n;i++)
{
memset(dp[now],0,sizeof(dp[now]));//
for(int s=0;s<=S;s++)
{
if(dp[pre][s]==0)//没加TLE
continue;
for(int x=1;x<=min(L,k);x++)
{
int st=calc(s,x);
dp[now][st]=(dp[now][st]+dp[pre][s])%mod;
}
dp[now][s]=(dp[now][s]+(dp[pre][s]*cnt)%mod)%mod;
}
swap(now,pre);
}
ll ans=0;
for(int i=0;i<=S;i++)
{
if((i>>(k-1))&1)
ans=(ans+dp[pre][i])%mod;
}
printf("%lld\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: