题目

题目链接:http://noip.ybtoj.com.cn/contest/102/problem/4
【YbtOJ#20070】诗人小 K_i++

思路

\(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;
}