ABC457F Second Gap 题解

ABC457F Second Gap

给定 \(N\) 和序列 \(D_1,\dots,D_{N-1}\)\(1\le D_i\le N-i\))。求排列 \(P_1,\dots,P_N\) 的个数(模 \(998244353\)),满足:对每个 \(i\),后缀 \(P_i,\dots,P_N\) 中最大值与次大值的位置差的绝对值 \(=D_i\)\(2\le N\le2\times10^5\)

可以从右往左考虑,定义 \(dp_{i,j}\) 为考虑 \(P_i,\dots,P_N\) 这些元素中最大值在 \(j\) 位置的方案数(即一个长 \(N-i+1\) 排列,满足 \(D_i,\dots D_{N-1}\) 这些关系的排列数)。现在考虑加入 \(P_{i-1}\),这个数的可能取值有 \(N-i\) 种。

如果 \(D_{i-1}\neq D_{i}\),则意味着 \(P_{i-1}\) 改变了 \(D\) 值,一定作为新的排列的次大值或最大值存在,而且可以确定原先排列最大值的位置。

  • 取最大值时 \(dp_{i-1,i-1}\leftarrow dp_{i,i-1+D_{i-1}}\)
  • 取次大值时 \(dp_{i-1,i-1+D_{i-1}}\leftarrow dp_{i,i-1+D_{i-1}}\)
  • 其余的 \(dp_{i-1}\) 值全为零,因为最大值不可能为其他位置。

如果 \(D_{i-1}=D_i\),则 \(P_{i-1}\) 不仅可以作为新的排列的次大值或最大值存在;还能作为其他值存在,且不改变排列中最大值的位置。故

  • 取最大值时 \(dp_{i-1,i-1}\leftarrow dp_{i,i-1+D_{i-1}}\)
  • 取次大值时 \(dp_{i-1,i-1+D_{i-1}}\leftarrow dp_{i,i-1+D_{i-1}}\)
  • 取其他值时 \(dp_{i-1,j}\leftarrow (N-i-2)dp_{i,j}\)

最后答案即为 \(\sum_{j=1}^{n} dp_{1,j}\)

这是一个二维 DP,但由于唯一一种转移 \(dp_{i-1,k}\leftarrow (N-i-2)dp_{i,k}\) 是所有下标位置乘上一个定值,我们可以给全局维护一个标记表示总乘积,每次给标记乘上 \((N-i-2)\),同时给每次单点改变值的转移乘上一个系数 \(\frac{1}{N-i-2}\)。同时 \(D_{i-1}\neq D_i\) 时清零的操作可以暴力做,因为每个位置作为最大值只会被转移 \(O(1)\) 次,因此从的删除位置数是 \(O(n)\) 的。

如果比较烧脑的话也可以使用线段树(区间乘,单点加)来做。

const int N=500005;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll P=998244353;
inline ll fpr(ll b,ll t=P-2,ll x=1ll){
	for(;t;t>>=1ll,b=b*b%P)
		if(t&1ll)x=x*b%P;
	return x;
}

ll dp[N],n,d[N],tag=1ll;
int stk[N],top;
void add(int p,ll v){
	dp[p]=(dp[p]+v)%P;
	stk[++top]=p;
}
void clear(){
	for(int i=1;i<=top;++i)
		dp[stk[i]]=0;
	top=0;
}
int main(){
	scanf("%lld",&n);
	for(int i=1;i<n;++i)scanf("%lld",&d[i]);
	if(d[n-1]!=1){
		puts("0");
		return 0;
	}
	add(n,1);
	for(int i=n-1;i>=1;--i){
		int p=i+d[i];ll v=dp[p];
		if(d[i]!=d[i+1]){
			clear();
			add(i,v);
			add(p,v);
		}else{
			tag=tag*(n-i-1)%P;
			v=v*fpr(n-i-1);
			add(i,v);
			add(p,v);
		}
	}
	ll sum=0;
	for(int i=1;i<=n;++i)sum=(sum+dp[i])%P;
	sum=sum*tag%P;
	printf("%lld\n",sum);
	return 0;
}
posted @ 2026-06-01 15:24  BigSmall_En  阅读(5)  评论(0)    收藏  举报