[冲刺国赛2022] 模拟赛5

题目描述

对于一个长度为 n 的排列 p,如果 pi=2pi+1,pi+1=2pi,那么 ai=1;否则 ai=0

现在给定 a 数组,问有多少个排列可以生成这样的 a

n40

解法

考虑把 n 个数划分为若干个二倍链(比如 1,2,4,8,16,32),然后把链的其中一段拆出来放到原序列中,就可以产生 a 序列中连续的一段 1

我们按顺序扫描 a 数组 dp,因为相同长度的链是等价类,所以我们只需要记录所有长度对应链的数量,状态数非常少。此外我们还要记录上一个填入的数,导致分裂出了两条链 x,y 。假设现在考虑到了位置 p,转移:

  • 如果 ap=0,那么枚举填入哪个数,注意这个数不能是 x 的尾部 或者 y 的头部,要不然会产生 1;然后把这个数的链分裂开来,更改链长分布的同时记录新的 x,y
  • 如果 ap=1,因为要和上一个位置形成二倍关系,所以只能用 x,y,那么讨论一下用哪一个即可。

还是需要把状态哈希之后存进 map 中,要用的时候再重新解密,时间充裕,怎么写都可以。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
using namespace std;
const int M = 45;
const int MOD = 1e9+7;
#define ll long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,a[M];map<ll,int> dp;
struct node
{
	int b[7],x,y;
	node() {x=y=0;memset(b,0,sizeof b);}
}z;
void add(int &x,int y) {x=(x+y)%MOD;}
ll encode(node s)
{
	ll r=0;
	for(int i=6;i>=1;i--) r=r*41+s.b[i];
	r=r*7+s.x;r=r*7+s.y;
	return r;
}
node decode(ll r)
{
	node s;
	s.y=r%7;r/=7;
	s.x=r%7;r/=7;
	for(int i=1;i<=6;i++) s.b[i]=r%41,r/=41;
	return s;
}
signed main()
{
	freopen("book.in","r",stdin);
	freopen("book.out","w",stdout);
	n=read();
	for(int i=1;i<n;i++)
		scanf("%1d",&a[i]);
	for(int i=1;i<=n;i++) if(i&1)
	{
		int l=0;
		for(int j=i;j<=n;j<<=1) l++;
		z.b[l]++;
	}
	for(int i=1;i<=n;i++) if(i&1)
	{
		int l=0;
		for(int j=i;j<=n;j<<=1) l++;
		for(int j=1;j<=l;j++)
		{
			int x=j-1,y=l-j;node t=z;
			t.b[l]--;t.b[x]++;t.b[y]++;
			t.x=x;t.y=y;
			dp[encode(t)]++;
		}
	}
	while(!dp.empty())
	{
		ll h=dp.rbegin()->first;
		int c=dp.rbegin()->second;
		dp.erase(--dp.end());
		if(!h) {printf("%d\n",c);return 0;}
		node s=decode(h);int l=n,r=0;
		for(int i=1;i<=6;i++) l-=s.b[i]*i;
		if(a[l]==0)
		{
			for(int i=1;i<=6;i++) if(s.b[i])
				for(int j=1;j<=i;j++)
				{
					int w=s.b[i]-(s.x==i)-(s.y==i);
					node t=s;t.b[i]--;
					t.b[t.x=j-1]++;t.b[t.y=i-j]++;
					add(dp[encode(t)],1ll*c*(w+
					(s.x==i&&j<i)+(s.y==i&&j>1))%MOD);
				}
		}
		else
		{
			r=l;while(a[r]==a[l]) r++;l=r-l;
			if(s.x>=l)
			{
				node t=s;t.b[t.x]--;
				t.x-=l;t.y=0;t.b[t.x]++;
				add(dp[encode(t)],c);
			}
			if(s.y>=l)
			{
				node t=s;t.b[t.y]--;
				t.y-=l;t.x=0;t.b[t.y]++;
				add(dp[encode(t)],c);
			}
		}
	}
	puts("0");return 0;
}
posted @   C202044zxy  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-06-04 CF1276F Asterisk Substrings
点击右上角即可分享
微信分享提示