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;
}

浙公网安备 33010602011771号