Examples

P8554 心跳 解题报告

P8554 心跳 解题报告:

更好的阅读体验

题意

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

\(1\leqslant m\leqslant n\leqslant 2000\)

分析

hzr 场切了,有点恐怖的。

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

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

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

  • 若去除的位置不是前缀最大值,\(f(p,i)=g(p)\)
  • 否则,\(f(p,i)\)\(i\) 后面大于 \([1,i-1]\) 所有数的单调栈大小加 \(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\) 序列,算重的原因实际上就是 \(201\text{x}\)\(012\text{x}\) 等价,以及 \(01\text{x}\)\(22\) 等价(\(\text{x}=\{0,2\}\)),于是我们只需保证序列中不存在 \(201\text{x}\) 子段与 \(22\) 后的 \(01\text{x}\) 子段即可。

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

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

复杂度 \(O(n^2)\)

代码

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

#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 @ 2022-09-25 17:42  xiaoziyao  阅读(184)  评论(0编辑  收藏  举报