首先设定一个数组Q对应每一个排列P,Qi指这个数在Pi~Pn中Pi的排名
我们可以发现每个数组Q可以唯一重构一个排列P,这个证明有点复杂,可以看这里
然后再根据后缀最大值的出现位置,即Qi=1的位置考虑哪些数组Q对应的排列P合法
<1>如果Qi=1,则Pi是后缀最大值,所以后缀次大值就是Pi+di,所以需要令Qi+1,Qi+2...,Qi+di−1>Qi+di=1
<2>如果Qi=2,则Pi是后缀次大值,所以后缀最大值就是Pi+di,所以需要令Qi+1,Qi+2...,Qi+di−1>Qi+di=1
<3>如果Qi=1,2,则Pi的加入并不会影响最大值或次大值的位置,则满足di=di+1,此时Qi可以是3,4,...n−i+1中任意选择
我们记fi,j是满足:"确定了Qi,Qi+1...Qn后,j满足Qj=1中下标的最小值"这个状态的方案数
所以容易根据上述情况转移
<1>fi,i=fi+1,i+di
<2>fi,i+di=fi+1,i+di
<3>若di=di+1,fi,j=fi+1,j×(n−i−1)
暴力转移为O(n2),考虑滚动数组为gi且先处理操作3,则我们的操作就可以转化为
<1>记录val=gi+di=fi,i+di
<2>将整个数组g乘上(n−i+1)(操作3)
<3>将两点gi和gi+di加上val(操作1,2)
单点查询+全局乘法+单点修改,套线段树即可
AC代码
Details
#include<bits/stdc++.h>
using namespace std;
#define QAQ return
#define QWQ return 0;
#define ll long long
#define pii pair< int,int >
const int N=2e5+10, mod=998244353;
struct segment_tree
{
int l, r;
ll val, tag;
inline segment_tree(){ val=0, tag=1; }
}st[2][N<<2];
inline void build( int p,int x,int l,int r )
{
st[p][x].l=l,st[p][x].r=r;
if( l == r ) QAQ ;
int mid=l+r>>1;
build( p,x<<1,l,mid );
build( p,x<<1|1,mid+1,r );
}
inline void maketag( int p,int x,ll v ){ ( st[p][x].val*=v )%=mod; ( st[p][x].tag*=v )%=mod; }
inline void pushdown( int p,int x )
{
if( st[p][x].tag != 1 )
{
maketag( p,x<<1,st[p][x].tag );
maketag( p,x<<1|1,st[p][x].tag );
st[p][x].tag=1;
}
}
inline void change( int p,int x,int pos,ll v )
{
if( st[p][x].l == st[p][x].r ){ ( st[p][x].val+=v )%=mod; QAQ ; }
pushdown( p,x );
int mid=st[p][x].l+st[p][x].r>>1;
if( mid >= pos ) change( p,x<<1,pos,v );
else change( p,x<<1|1,pos,v );
}
inline ll query( int p,int x,int pos )
{
if( st[p][x].l == st[p][x].r ){ QAQ st[p][x].val; }
pushdown( p,x );
int mid=st[p][x].l+st[p][x].r>>1;
if( mid >= pos ) QAQ query( p,x<<1,pos );
else QAQ query( p,x<<1|1,pos );
}
ll d[N];
int main()
{
ios::sync_with_stdio( false );
cin.tie( nullptr ), cout.tie( nullptr );
int n;
cin>>n;
for( int i=1;i<n;i++ ) cin>>d[i];
if( d[n-1] != 1 ){ cout<<0; QWQ; }
build( 0,1,1,n ); build( 1,1,1,n );
change( 0,1,n,1 ); change( 1,1,n-1,1 );
for( int i=n-1;i>=2;i-- )
{
ll val1=0, val2=0, val3=0;
int pos=d[i-1]+i-1;
if( pos <= n )
val2=query( 0,1,pos ), val3=query( 1,1,pos ),
val1=( val2+val3 )%mod;
if( d[i] == d[i-1] ) ( st[0][1].tag*=( n-i ) )%=mod, ( st[1][1].tag*=( n-i ) )%=mod;
else st[0][1].tag=st[1][1].tag=0;
if( pos <= n )
change( 0,1,pos,val1 ),
change( 1,1,i-1,val2+val3 );
}
ll ans=0;
for( int i=1;i<=n;i++ ) ( ans+=query( 0,1,i )+query( 1,1,i ) )%=mod;
cout<<ans;
QWQ
}
时间复杂度:O(nlogn),瓶颈在于线段树,但是据说是可以使用区间乘记录的方式优化到O(n)