1. 程式人生 > >our happy ending(狀壓dp)

our happy ending(狀壓dp)

bsp 相加 col amp int space ring %d printf

題意:給定一個n,k,l。

問有多少長度為n的序列滿足選出一些數使得他們相加為k,數列中每個數都在1-l以內。

Solution

正解還是很妙的。

狀壓dp,設dp[i][j]表示長度為i的序列,能表示出集合為j的序列個數。

這個狀態非常好,我們每局下一個可填的數,可選集合就變成了j|(1<<p-1)|(j<<p&size)

Code

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod=1e9+7; ll dp[1<<20],ans; int n,k,l,t,ma; int main(){ scanf("%d",&t); while(t--){ scanf("%d%d%d",&n,&k,&l); memset(dp,0,sizeof(dp)); ma=(1<<k)-1;dp[0]=1; for(int i=1;i<=n;++i) for(int j=ma;j>=0;--j)if(dp[j]){ ll pu
=dp[j]; for(int p=1;p<=min(k,l);++p) { int x=(1<<p-1)|j|((j<<p)&ma); dp[x]+=pu; if(dp[x]>mod)dp[x]-=mod; } if(l>k){ dp[j]+=(pu*(l-k))%mod; if(dp[j]>mod)dp[j]-=mod; } }ans
=0; for(int i=1;i<=ma;++i)if(i&(1<<k-1)){ans+=dp[i];if(ans>mod)ans-=mod;} printf("%lld\n",ans); } return 0; }

our happy ending(狀壓dp)