把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【状压】ARC058E 和風いろはちゃん / Iroha and Haiku

题目链接

是我想不到的的状压方式。

首先,条件中的“存在”比较麻烦,由于一个相同的数列可能包含多个满足条件的三元组,用乘法原理直接正面刚会出现重复计数的情况。

正难则反,所以我们反向考虑用总的10n的方案数减去不合法的数列。


注意到X+Y+Z17,所以可以考虑状压。

状压的方式非常不容易想到,具体是:

我们用二进制位数来表示数字,例如:5>10000 , 3>100

如果5后面是加上一个3,即序列的一个后缀是53,那么这个状态就用10000100表示(把两个数的二进制表示拼在一起)。同时,这个新的二进制状态的第8位是1,表示5+3=8;第3位为1,表示最后一个数是3,那么倒数第二个数就可以用83=5算出来。

举个更复杂的例子,假如后缀是536(五三打钱),我们的二进制状态就是10000100100000,那么我们可以通过这个二进制状态得到所有536这个后缀的所有情况:

14(第14位上为114=5+3+6

9(第9位上为19=3+6

6(第6位上为16=6

5149=5=5

8146=8=5+3

396=3=3

(这实际上就是一种变相的后缀和)

那么什么样的状态表示俳句呢?

俳句是连续三段数,和分别为X,Y,Z,所以俳句对应的二进制第X+Y+Z,Y+Z,Z位上都是1

用这种方式表示状态,状态的最大位数就是最大和,所以状态数是2X+Y+Z种,在时空承受范围之内。


关于dp过程,设dp[i][s]表示转移到第i位,数列结尾的状态为s的方案数。

转移时枚举后继数字(第i+1位)为j,那么新的二进制状态就是s=(s<<j)|(1<<(j1))

则有dp[i+1][s]+=dp[i][s]

边界情况dp[0][0]=1

另外,这里有一点需要提一下,就是n的范围比较大,而每个数字的范围在[1,10],那么最大的数字和应该是40,但是我们只需要dp和是x+y+z的部分,因为这个是后缀和,前面的数不管是啥并不重要,我们的i指针在不断移动的时候,就计算了不同位置的方案。


►Code View

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<map>
#include<cmath>
using namespace std;
#define N 45
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define LL long long
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
	return f*x;
}
int n,x,y,z;
LL dp[N][1<<18];
LL ksm(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=res*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return res;
}
int main()
{
	n=rd(),x=rd(),y=rd(),z=rd();
	int jdg=((1<<(x+y+z-1))|(1<<(y+z-1))|(1<<(z-1)));
	dp[0][0]=1;
	int sum=(1<<(x+y+z))-1;//最高位是x+y+z-1 所以最大和就是所有位数都是1 也就是2^(x+y+z)-1 
	for(int i=0;i<=n-1;i++)
		for(int s=0;s<=sum;s++)
		{
			if(dp[i][s]==0) continue;//不会产生贡献
			for(int j=1;j<=10;j++)//枚举第i+1位是什么数字
			{
				int t=(s<<j)|(1<<(j-1));
				t&=sum;//防止溢出
				if((t&jdg)==jdg) continue;//存在俳句
				else dp[i+1][t]=(dp[i+1][t]+dp[i][s])%MOD;
			}
		}
	LL ans=0;
	for(int s=0;s<=sum;s++)
		ans=(ans+dp[n][s])%MOD;
	ans=(ksm(10,n)-ans+MOD)%MOD;
	printf("%lld\n",ans);
    return 0;
}
posted @   Starlight_Glimmer  阅读(125)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2019-10-22 CF C.Ivan the Fool and the Probability Theory【思维·构造】
2019-10-22 CF The World Is Just a Programming Task (Easy Version)【分析·思维】
2019-10-22 AGC035 A - XOR Circle【分析】
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示