NOIP2023模拟5联测26 零二

文章讨论了一个关于长度为n的序列A,通过小根堆操作将其元素重排成B的问题。通过递归和子问题划分,分析了不同元素在序列B中的位置关系,得出总的不同排列数,时间复杂度为O(n^4)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

有一个长度为nnn的序列AAA,你可以将AAA中的元素进行重排得到一个新的序列BBB,规则如下:

  • BBB初始为空,同时维护一个初始为空的小根堆TTT,然后进行以下两类操作各nnn次:
    • 将当前AAA的第一个元素删除并加入小根堆TTT
    • 将小根堆的堆顶删除并加入BBB的末尾,需要保证TTT非空

操作的顺序是任意的,问总共可能得到多少种不同的BBB,输出答案模109+710^9+7109+7后的值。

1≤n≤100,1≤Ai≤n1\leq n\leq 100,1\leq A_i\leq n1n100,1Ain


题解

注意到1≤ai≤n1\leq a_i\leq n1ain,我们先考虑如果AiA_iAi111nnn的一个排列该怎么求解。

我们设nnnAAA序列和BBB序列中出现的位置分别为p,qp,qp,q,即Ap=Bq=nA_p=B_q=nAp=Bq=n。因为TTT是小根堆,且AAA111nnn的一个排列,所以当拿出nnn时就意味着TTT已经被搬空了。由此可得,BBB的前qqq个元素就是AAA的前qqq个元素,只是顺序可能不太一样。所以,{A1,A2,…,Aq}={B1,B2,…,Bq}\{A_1,A_2,\dots,A_q\}=\{B_1,B_2,\dots,B_q\}{A1,A2,,Aq}={B1,B2,,Bq},且{Aq+1,Aq+2,…,An}={Bq+1,Bq+2,…,Bn}\{A_{q+1},A_{q+2},\dots,A_n\}=\{B_{q+1},B_{q+2},\dots,B_n\}{Aq+1,Aq+2,,An}={Bq+1,Bq+2,,Bn}

既然我们已经知道Bq=nB_q=nBq=n,那么我们可以把原问题看作两个子问题:[1,q−1][1,q-1][1,q1][q+1,n][q+1,n][q+1,n]。其中B1,B2,…,Bq−1B_1,B_2,\dots,B_{q-1}B1,B2,,Bq1是由A1,A2,…,AqA_1,A_2,\dots,A_qA1,A2,,Aq去掉ApA_pAp再经过一些操作变化而来的,而Bq+1,Bq+2,…,BnB_{q+1},B_{q+2},\dots,B_nBq+1,Bq+2,,Bn是由Aq+1,Aq+2,…,AnA_{q+1},A_{q+2},\dots,A_nAq+1,Aq+2,,An经过一些操作变化而来的。

我们考虑递归地解决子问题。

因为在每次将一个问题分为子问题时都是将一个最大值删去,再分成两个部分,那么每个子问题所对应的子序列A′A'A都是原序列AAA的某一段区间[l,r][l,r][l,r]删去一些较大的数而得到的。

我们考虑DPDPDP,设fl,r,if_{l,r,i}fl,r,i表示Al,Al+1,…,ArA_l,A_{l+1},\dots,A_rAl,Al+1,,Ar中不超过iii的数构成的子序列的方案数。设Am=iA_m=iAm=i,下面考虑转移:

  • mmm不在[l,r][l,r][l,r]上时,显然fl,r,i=fl,r,i−1f_{l,r,i}=f_{l,r,i-1}fl,r,i=fl,r,i1
  • mmm[l,r][l,r][l,r]上时,我们枚举iiiBBB中的位置,可得
    fl,r,i=∑x=mrfl,x,i−1⋅fx+1,r,i−1f_{l,r,i}=\sum\limits_{x=m}^rf_{l,x,i-1}\cdot f_{x+1,r,i-1}fl,r,i=x=mrfl,x,i1fx+1,r,i1

那我们再来考虑如何处理AAA不是111nnn的排列的情况。

我们约定,当小根堆的堆顶为iii,并且堆中存在多个iii,那么我们取的时候先取下标最小的。

那在我们的约定下,可以将原数列转化为一个排列。

考虑这样转化的正确性。转化后AAA中的数和BBB中的数一一对应,而且BBB中相同的数的先后顺序和在AAA中的先后顺序是相同的,也就是说先后顺序一定,所以这样转化是可行的。

时间复杂度为O(n4)O(n^4)O(n4),这跑不满,是可以过的。

code

#include<bits/stdc++.h>
using namespace std;
const int N=100;
const long long mod=1e9+7;
int n,a[N+5],b[N+5],cnt[N+5],rk[N+5];
long long f[N+5][N+5][N+5];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);++cnt[a[i]];
	}
	for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--){
		b[i]=cnt[a[i]]--;rk[b[i]]=i;
	}
	for(int i=1;i<=n+1;i++){
		for(int j=0;j<=n+1;j++) f[i][i-1][j]=1;
	}
	for(int l=n;l>=1;l--){
		for(int r=l;r<=n;r++){
			f[l][r][0]=1;
			if(b[l]<b[r])
			for(int i=1;i<b[r];i++) f[l][r][i]=f[l][r-1][i];
			else
			for(int i=1;i<b[l];i++) f[l][r][i]=f[l+1][r][i];
			for(int i=max(b[l],b[r]);i<=n;i++){
				if(rk[i]<l||rk[i]>r) f[l][r][i]=f[l][r][i-1];
				else{
					for(int j=rk[i];j<=r;j++){
						if(b[j]<=i)
						f[l][r][i]=(f[l][r][i]+f[l][j][i-1]*f[j+1][r][i-1])%mod;
					}
				}
			}
		}
	}
	printf("%lld",f[1][n][n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值