题目
题目链接:http://noip.ybtoj.com.cn/contest/102/problem/4
思路
设 (f[s][i]) 表示 (i-(x+y+z)+1sim i) 的所有后缀中,能构成合法的和的集合。
定义一个和是合法的,当且仅当等于一段后缀且能没有“跨过” (x) 和 (y)(也就是这个后缀存在一段前缀和为 (x) 和 (y))。
那么先预处理出 (g[s][i]) 表示状态 (s) 在加入 (i) 这个数之后,可以得到的状态。
设 (f[s][i]) 表示到了第 (i) 为,状态为 (s) 的方案数。那么 (f[s][i]) 可以转移到 (f[g[s][k]][i+1])。
然后最终 (sum^{maxn-1}_{i=2^z}f[i][n]) 就是答案。
时间复杂度 (O(2^zmn+2^zmz))。
代码
#include <bits/stdc++.h>
#define reg register
using namespace std;
const int N=45,MAXN=(1<<17)+10,MOD=1e9+7;
int n,x,y,z,ans,Maxn,f[MAXN][N],g[MAXN][N];
int main()
{
freopen("poem.in","r",stdin);
freopen("poem.out","w",stdout);
scanf("%d%d%d%d",&n,&x,&y,&z);
y+=x; z+=y; Maxn=(1<<z);
for (reg int s=0;s<Maxn;s++)
for (reg int i=1;i<=10;i++)
{
for (reg int j=0;j<z;j++)
if (s&(1<<j))
{
if (i+j+1<=x) g[s][i]|=1<<(i+j);
if (i+j+1<=y && j+1>=x) g[s][i]|=1<<(i+j);
if (i+j+1<=z && j+1>=y) g[s][i]|=1<<(i+j);
}
if (i<=x) g[s][i]|=(1<<i-1);
if (s&(1<<z-1)) g[s][i]=Maxn-1;
}
f[0][0]=1;
for (reg int i=0;i<n;i++)
for (reg int s=0;s<Maxn;s++)
{
if (!f[s][i]) continue;
for (reg int j=1;j<=10;j++)
f[g[s][j]][i+1]=(f[g[s][j]][i+1]+f[s][i])%MOD;
}
for (int s=(1<<z-1);s<Maxn;s++)
ans=(ans+f[s][n])%MOD;
printf("%d",ans);
return 0;
}