其他分享
首页 > 其他分享> > 【AT1975】 [ARC058C] 和風いろはちゃん(状压dp)

【AT1975】 [ARC058C] 和風いろはちゃん(状压dp)

作者:互联网

原题链接

题意

若 \(a=\{a_1,a_2,\cdots a_n\}\) 存在 \(1\le x<y<z<w\le n+1\) 满足 \(\sum\limits_{i=x}^{y-1}a_i=X,\sum\limits_{i=y}^{z-1}a_i=Y,\sum\limits_{i=z}^{w-1}a_i=Z\) 时,则称数列 \(a\) 是好的

-求在所有长度为 \(n\) 且 \(a_i\in\mathbb{N}^{+}\cap[1,10]\) 的 \(10^n\) 个序列 \(a\) 中,有多少个序列是好的,答案对 \(10^9+7\) 取模。

数据范围

\(3\le n\le40\),\(1\le X\le5\),\(1\le Y\le7\),\(1\le Z\le5\)。

思路

发现本题直接统计合法的序列很难解决去重问题,考虑容斥,即用总的方法数出不合法的方案数。

由于 \(a \in [1,10]\),总的方案数就是 \(10^n\)。注意到 \(X+Y+Z \leq 17\)。可以考虑状压。

假设当前枚举的数为 \(i\),那么加上这个数后序列是否合法,就是看是否原序列的后缀是否满足 \(x+y+z-i\),\(y+z-i\),\(z-i\) 都出现了。那么就可以设 \(f[i][j]\) 表示当前枚举到第 \(i\) 个数,当前枚举的序列后缀状态为 \(j\) 时的不合法序列方案数。

对于一个状态 \(j\),它在二进制表示下的第 \(i\) 位为 \(1\) 就表示存在一个后缀为 \(i\),当加入一个新的数 \(k\) 时,后缀 \(i\) 就更新为了 \(k\),只需要将原状态向前移动 \(k\) 位再把 \(2^k\) 加进状态即可。由于 \(X+Y+Z \leq 17\),大于 \(17\) 的后缀就没有必要考虑了,可以直接略去。而为了避免出现合法序列,自然后缀中不能同时出现 \(x+y+z\),\(y+z\) 和 \(z\),在转移的时候只需要判断一下当前状态是否包含了该合法状态即可。

code:

#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
const int N=55,mod=1e9+7,M=1<<20;
int n,m,x,y,z,f[N][M],ans;
int main()
{
//	freopen("seq.in","r",stdin);
//	freopen("seq.out","w",stdout);
	scanf("%d%d%d%d",&n,&x,&y,&z);ans=1;m=(1<<x+y+z)-1;
	for(int i=1;i<=n;i++) ans=1ll*ans*10%mod;
	int state=((1<<x+y+z-1))|((1<<y+z-1))|(1<<z-1);f[0][0]=1;
	for(int i=1;i<=n;i++)
	    for(int j=0;j<=m;j++)
	        if(f[i-1][j]&&(j&state)!=state)
	            for(int k=1;k<=10;k++)
	            {
	            	f[i][((j<<k)|(1<<k-1))&m]+=f[i-1][j];
	            	f[i][((j<<k)|(1<<k-1))&m]%=mod;
				}
	for(int i=0;i<=m;i++)
	    if((i&state)!=state)
	    {
	    	ans=(ans-f[n][i]+mod)%mod;
		}
	printf("%d\n",ans);
	return 0;
}

标签:10,le,后缀,合法,ARC058C,include,序列,AT1975,dp
来源: https://www.cnblogs.com/NLCAKIOI/p/16508458.html