你有一个 的排列 。
你可以进行一次如下操作:选择一个非负整数 ,以及 个不交的区间 ,然后对每个 的 ,将 替换为它们的升序排列。
你想知道 在操作完后可能有多少种不同的情况。由于情况数可能很多,所以你只想得到其对 取模后的结果。请你帮帮你自己求出答案!
。
考虑设 表示 在操作之后有多少种不同的情况,考虑转移。一个区间 只能通过操作 来排序当且仅当不存在 满足 且排序 再排序 得到的序列和排序 相同。不妨称满足条件的区间是 ”好的“
那么设 表示所有满足 是好的的 的集合,那么有转移 ,初始状态 。
考虑求解 ,显然有一个 的算法:
对于每个 ,从 开始往前遍历,并维护当前 中最长的区间 和其中的最小值 。刚开始 ,每遍历到一个位置 :
- 若 ,什么也不做;
- 若 ,表明排序 之后会有一些 中的元素跑到 前面,由于 已经是好的了,所以这是排序 再排序 所做不到的,那么更新 ,并把 加入 ,更新 ;
观察到许多 是有大量重复的,考虑利用这种高重复性优化时间复杂度。设 为满足 且 的最大的 , 为满足 且 的最大的 。那么有 且 。
更进一步,设 为 中最小值的位置,由于 ,所以有 ,那么有 。并且观察到从 和从 开始遍历到 时都有 ,所以 。
那么二分 + ST 表找到 ,拿个前缀和维护转移即可。时间复杂度 。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=200005,BS=20;
const int p=998244353;
int n,a[S];
int tot,sta[S],premn[S];
int mlog[S],mn[S][BS+5],mx[S][BS+5];
int sum[S],dp[S];
inline int quemn(int l,int r)
{
int k=mlog[r-l+1];
int x=mn[l][k],y=mn[r-(1<<k)+1][k];
return a[x]<a[y]?x:y;
}
inline int quemx(int l,int r)
{
int k=mlog[r-l+1];
int x=mx[l][k],y=mx[r-(1<<k)+1][k];
return a[x]>a[y]?x:y;
}
inline int fnd(int x,int val)
{
int l=1,r=x,res=0;
while(l<=r)
{
int mid=l+r>>1;
if(a[quemx(mid,x)]>val) res=mid,l=mid+1;
else r=mid-1;
}
return res;
}
inline int quesm(int l,int r)
{
return (sum[r]-(l==0?0:sum[l-1])+p)%p;
}
int main()
{
freopen("bai.in","r",stdin);
freopen("bai.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sta[tot=0]=0;
for(int i=1;i<=n;i++)
{
while(tot>0&&a[sta[tot]]>a[i]) tot--;
premn[i]=sta[tot];
sta[++tot]=i;
}
mlog[0]=-1;
for(int i=1;i<=n;i++) mlog[i]=mlog[i>>1]+1,mn[i][0]=mx[i][0]=i;
for(int j=1;j<=mlog[n];j++)
{
for(int i=1;i<=n-(1<<j)+1;i++)
{
int x=mn[i][j-1],y=mn[i+(1<<j-1)][j-1];
mn[i][j]=a[x]<a[y]?x:y;
x=mx[i][j-1],y=mx[i+(1<<j-1)][j-1];
mx[i][j]=a[x]>a[y]?x:y;
}
}
sum[0]=dp[0]=1;
for(int i=1;i<=n;i++)
{
int x=premn[i],y=fnd(x,a[i]);
if(y==0) dp[i]=quesm(x,i-1);
else
{
int z=quemn(y+1,x);
dp[i]=((dp[z]-quesm(y,z-1)+p)%p+quesm(x,i-1))%p;
}
sum[i]=(sum[i-1]+dp[i])%p;
}
printf("%d\n",dp[n]);
return 0;
}