Examples

P8554 心跳 解题报告

P8554 心跳 解题报告:

更好的阅读体验

题意

定义一个排列上的函数 f(p,i) 表示序列 p 去除 i 位置后前缀最大值个数。对于所有长度为 n 的排列 p,生成序列 ai=f(p,i)。试计数值域 [m,n],且能被生成的 a 序列数量。

1mn2000

分析

hzr 场切了,有点恐怖的。

下面记 g(p) 为不去除任何位置的前缀最大值个数。

首先有一个结论,对于两个 g 不相等的排列,其生成的 a 序列不可能有交。

经过初步地观察,我们能得知:

  • 若去除的位置不是前缀最大值,f(p,i)=g(p)
  • 否则,f(p,i)i 后面大于 [1,i1] 所有数的单调栈大小加 g(p)1

于是我们可以将序列里的数分成三个部分:前缀最大值,可能成为前缀最大值的数(下文称其为有用的数),其他。

将排列按照前缀最大值划分成若干段,那么前缀最大值的 f(p,i) 就是其对应段有用的数数量加上 g(p)1

我们并不关心有用的数的位置,只关心其数量,于是可以将一段内所有有用的数保留顺序提前,然后生成一个仅包含 {0,1,2} 的序列,0 对应前缀最大值,1 对应有用的数,2 对应没用的数。(称这种序列为颜色序列)

我们想直接 dp 来计数颜色序列,但必须先知道任意颜色序列是否都存在一个对应的排列 p

考虑一种排列的生成方式:从前往后加入数字,维护目前前缀 [1,i] 离散化后的结果,每次插入一个 [1,i+1] 内的数字并将大于等于其的数字加一(其实就是在值域上插入数字)。那么插入 i+1 就对应插入一个 0,插入 i 就对应插入一个 1,否则就是插入一个 2

只需保证第一个位置是 0,第二个位置是 0/1 即可。

但是不同的颜色序列可能对应相同的 a 序列,算重的原因实际上就是 201x012x 等价,以及 01x22 等价(x={0,2}),于是我们只需保证序列中不存在 201x 子段与 22 后的 01x 子段即可。

fi,j,0/1/2/3,0/1/2 表示 dp 了 i 个位置,前缀最大值个数为 j,上一个为 01(长度 =1)、1(长度 >1)、2,无限制、当前段不能只有 11,后面所有段不能只有 11,直接转移即可。

m 的限制很好处理,要么前缀最大值数量大于 m,要么前缀最大值数量等于 m 且没有一个前缀最大值段没有有意义的数,随便改改转移方程就好了。

复杂度 O(n2)

代码

写不动,借鉴了官方题解的波特状态。

#include<stdio.h>
#include<string.h>
const int maxn=2005,mod=1000000007;
int n,m,ans,now;
int f[2][maxn][11],g[11],h[11];
void solve(int typ){
	now=0,f[0][1][1]=1;
	if(typ==0)
		f[0][2][0]=1;
	for(int i=3;i<=n;i++){
		now^=1,memset(f[now],0,sizeof(f[now]));
		for(int j=1;j<=i;j++){
			for(int c=0;c<=10;c++){
				g[c]=f[now^1][j][c];
				if(typ&&(c==0||c==5||c==8))
					g[c]=0;
			}
			h[0]=h[3]=(0ll+g[0]+g[1]+g[2]+g[8]+g[10])%mod;
			h[1]=f[now^1][j][0],h[2]=(g[1]+g[2])%mod;
			h[4]=(0ll+g[3]+g[4]+g[5]+g[7])%mod;
			h[5]=(0ll+g[4]+g[5]+g[7])%mod;
			h[6]=f[now^1][j][5],h[7]=(g[6]+g[7])%mod;
			h[8]=g[3];
			h[9]=f[now^1][j][8],h[10]=(g[9]+g[10])%mod;
			for(int c=0;c<=10;c++){
				int nxt=j+(c==0||c==5||c==8);
				f[now][nxt][c]=(f[now][nxt][c]+h[c])%mod,h[c]=0;
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	solve(0);
	for(int i=m+1;i<=n;i++)
		for(int c=0;c<=10;c++)
			if(c!=6&&c!=9)
				ans=(ans+f[now][i][c])%mod;
	memset(f,0,sizeof(f));
	solve(1);
	for(int c=0;c<=10;c++)
		if(c!=0&&c!=5&&c!=6&&c!=8&&c!=9)
			ans=(ans+f[now][m][c])%mod;
	printf("%d",ans);
	return 0;
}
posted @   xiaoziyao  阅读(201)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示