题解:CF2249B

题意

给出 nnn 与长度为 n−1n-1n−1 的序列 vvv,下标从 111 开始。求满足下面条件的长度为 nnn 的排列 ppp 的个数对 998244353998244353998244353 取模后的结果:

∀1≤i<n,vi=min⁡(max⁡1≤j≤ipj,max⁡i+1≤j≤npj) \forall 1 \le i<n,v_i=\min(\max_{1 \le j \le i} p_j,\max_{i+1 \le j \le n} p_j) ∀1≤i<n,vi=min(1≤j≤imaxpj,i+1≤j≤nmaxpj)

思路

首先,我们考虑 nnn 在排列中的位置 xxx,合法的 vvv 的性质。那么,

vi={max⁡1≤j≤ipj,i<xmax⁡i+1≤j≤npj,i≥x v_i= \begin{cases} \max_{1 \le j \le i} p_j,i<x \\ \max_{i+1 \le j \le n} p_j,i \ge x \end{cases} vi={max1≤j≤ipj,i<xmaxi+1≤j≤npj,i≥x

而 max⁡1≤j≤ipj\max_{1 \le j \le i} p_jmax1≤j≤ipj 是非递减的,max⁡i+1≤j≤npj\max_{i+1 \le j \le n} p_jmaxi+1≤j≤npj 是非递增的,所以 vvv 一定是先非降后非增的,否则答案为 000。

我们还容易发现,如果 i<x,vi=vali<x,v_i=vali<x,vi=val,那么 valvalval 必然会在 111 到 iii 中出现。而排列中 valvalval 只会出现一次,且 j<xj<xj<x,故不可能存在 j≥xj \ge xj≥x 满足 vj=valv_j=valvj=val。同理,如果 i≥x,vi=vali \ge x,v_i=vali≥x,vi=val,则不可能存在 j<xj<xj<x 满足 vj=valv_j=valvj=val。

另外,若 n−1n-1n−1 在排列中的位置在 nnn 的左边,那么有 vx−1=max⁡1≤i≤x−1pi=n−1v_{x-1}=\max_{1 \le i \le x-1} p_i=n-1vx−1=max1≤i≤x−1pi=n−1;否则 vx=max⁡x+1≤i≤npi=n−1v_x=\max_{x+1 \le i \le n} p_i=n-1vx=maxx+1≤i≤npi=n−1,即 n−1n-1n−1 一定出现在 vvv 中。同时,排列两部分不可能都含有 nnn,所以 nnn 不可能在 vvv 中。

综合以上诸性质,可得对于序列 vvv,存在合法排列的必要条件:

  • vvv 中含有 n−1n-1n−1
  • vvv 中不含 nnn
  • vvv 先非降后非增

且若 nnn 在排列中的位置为 xxx,那么需满足 vx−1v_{x-1}vx−1 与 vxv_xvx 有且仅有一个 n−1n-1n−1。(若有零个,那么违背了先升后降规则;若有两个,那么违背了左右不重复规则)

接下来考虑答案个数。

容易发现,对于一个 vvv,能满足 vx−1v_{x-1}vx−1 与 vxv_xvx 有且仅有一个 n−1n-1n−1 的 xxx 有两个,因为其先增后降,且最大值为 n−1n-1n−1。而这两种的答案个数是相等的(证明如下),所以我们只需求出 px=n,vx−1<n−1,vx=n−1p_x=n,v_{x-1}<n-1,v_x=n-1px=n,vx−1<n−1,vx=n−1 的答案个数再乘 222 即可。

::::info关于上述两个情况答案相等的证明

令一个合法答案排列 p1,p2,...,px1−1,n,px1,...,pn−1p_1,p_2,\dots,p_{x_1-1},n,p_{x_1},\dots,p_{n-1}p1,p2,...,px1−1,n,px1,...,pn−1 的对应排列为 p1,p2,...,px2−1,n,px2,...,pn−1p_1,p_2,\dots,p_{x_2-1},n,p_{x_2},\dots,p_{n-1}p1,p2,...,px2−1,n,px2,...,pn−1,那么 ∀x1≤i<x2,vi=n−1\forall x_1 \le i<x_2,v_i=n-1∀x1≤i<x2,vi=n−1。

分类讨论:1≤i<x11 \le i<x_11≤i<x1 或 x2≤i<nx_2 \le i<nx2≤i<n,显然两个排列的 viv_ivi 都为 max⁡1≤j≤ipj\max_{1 \le j \le i} p_jmax1≤j≤ipj;x1≤i<x2x_1 \le i<x_2x1≤i<x2,分成两部分后 nnn 与 n−1n-1n−1 一定不会被分到相同的区间里,所以两区间最大值的最小值即为 n−1n-1n−1。所以,若一排列满足 vvv 的限制,那么其对应排列也同样满足。反之同理。故两者互为充要条件。证毕。

::::

对于 1≤i<x1 \le i<x1≤i<x,若 vi−1<viv_{i-1}<v_ivi−1<vi,则说明区间 111 到 i−1i-1i−1 不含 viv_ivi 但区间 111 到 iii 包含之,所以 pip_ipi 必然等于 viv_ivi,情况数为 111。若 vi−1=viv_{i-1}=v_ivi−1=vi,那么 pip_ipi 一定在 111 到 viv_ivi 之间,且不能取已取过的数。x≤i≤nx \le i \le nx≤i≤n 同理。而左右两端中,可选择的限制相同,且可选择的值域区间不断变大(越接近 xxx,vvv 越大),所以用双指针维护现在取到的左端点与右端点,并不断向 xxx 逼近,分别终止于 x−1x-1x−1 与 xxx。最开始时答案为 111。每次逼近,以左段区间为例,若 vi−1<viv_{i-1}<v_ivi−1<vi,则答案不变;否则该数可取值的个数即为 viv_ivi 减去已遍历过的数的个数,并用答案乘之。然后将已取过的个数加一。

即有转移:

cpp 复制代码
while(nowl<nr||nowr>=nr){
	if(nowl<nr&&a[nowl]<a[nowr]||nowr==nr-1){
		if(nowl>1&&a[nowl]==now){
			if(now<=cnt){
				ans=0;
				break;
			}
			ans=ans*(now-cnt)%mod;
		}
		now=max(now,a[nowl]),nowl++;
	}else{
		if(nowr<n-1&&a[nowr]==now){
			if(now<=cnt){
				ans=0;
				break;
			}
			ans=ans*(now-cnt)%mod;
		}
		now=max(now,a[nowr]),nowr--;
	}
	cnt++;
}

最后记得乘 222。

完整代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int maxn=1e6+5,mod=998244353;
int n;
int a[maxn],b[maxn];
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++){
		b[i]=0;
	}
	for(int i=1;i<n;i++){
		cin>>a[i];
	}
	int nl=1,nr=n-1;
	while(nl<n-1&&a[nl]<=a[nl+1]){
		b[a[nl]]|=1;
		nl++;
	}
	while(nr>1&&a[nr-1]>=a[nr]){
		b[a[nr]]|=2;
		nr--;
	}
	b[a[nl]]|=1,b[a[nr]]|=2;
	if(nl<nr){
		cout<<"0\n";
		return;
	}
	if(b[n]||!b[n-1]){
		cout<<"0\n";
		return;
	}
	for(int i=1;i<n-1;i++){
		if(b[i]>2){
			cout<<"0\n";
			return;
		}
	}
	int ans=1;
	int nowl=1,nowr=n-1,cnt=0,now=0;
	while(nowl<nr||nowr>=nr){
		if(nowl<nr&&a[nowl]<a[nowr]||nowr==nr-1){
			if(nowl>1&&a[nowl]==now){
				if(now<=cnt){
					ans=0;
					break;
				}
				ans=ans*(now-cnt)%mod;
			}
			now=max(now,a[nowl]),nowl++;
		}else{
			if(nowr<n-1&&a[nowr]==now){
				if(now<=cnt){
					ans=0;
					break;
				}
				ans=ans*(now-cnt)%mod;
			}
			now=max(now,a[nowr]),nowr--;
		}
		cnt++;
	}
	cout<<ans*2%mod<<"\n";
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}
相关推荐
小玮看世界1 小时前
[Python]线段树与二分法
数据结构·算法
吹什么轩2 小时前
c++复习:map和set的使用
开发语言·c++
fpcc3 小时前
跟我学C++中级篇—内存流
开发语言·c++
Tisfy3 小时前
LeetCode 3345.最小可整除数位乘积 I:暴力枚举(从n开始尝试)
数学·算法·leetcode·题解·枚举
DolphinDB智臾科技3 小时前
告别 TB 级数据搬运:DolphinDB 滤波算法库,重塑物联网数据价值链
物联网·算法
吹什么轩4 小时前
c++复习:模拟实现list来更好的理解和应用list
开发语言·c++
AmyLin_20014 小时前
PDF 色彩保真工程实践【5】核心实现(下):拼装 Image XObject、页面资源与内容流
c++·pdf·sdk·颜色空间·印刷·icc·cmyk
可编程芯片开发4 小时前
基于双PI控制器结构的六步逆变器供电无刷直流电机调速simulink仿真
算法
Sagittarius_A*5 小时前
后量子密码|通识认知 02|量子威胁核心原理:Shor 算法如何击穿传统公钥密码
算法·信息安全·密码学·量子计算