【YbtOJ#20070】诗人小 K
题目
题目链接:http://noip.ybtoj.com.cn/contest/102/problem/4
思路
设 \(f[s][i]\) 表示 \(i-(x+y+z)+1\sim 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;
}