CF1591F
退役选手打了打 CF 玩,发现脑子不灵了。
考虑一个暴力 DP ,设 $f_{i,j}$ 表示处理到第 $i$ 个, $b_i = j$ 的方案数,则转移方程为:
$$
f_{i,j}=
\left\{
\begin{aligned}
\sum_{j'=1}^{b_{i-1}}f_{i-1,j'}-f_{i-1,j} (j \leq b_i) \\
0 (j > b_i)
\end{aligned}
\right.
$$
首先有一个关键结论:把 $a$ 数组排序,在 $(a_i,a_{i+1}]$ 区间的答案全部是相同的。因此,有用的 $j$ 其实只有 $O(n)$ 个。可以把 $j$ 的一维搬到线段树上,具体来说,以 $j$ 为下标 $f_{i,j}$ 为权值开一棵线段树。从 $i$ 转移到 $i+1$ 直接在线段树上修改。
注意对于同一个 $i$ 的所有 $j$ 来说, $\sum_{j'=1}^{b_{i-1}}f_{i-1,j'}$ 都是一个定值,于是这个线段树只用支持三种操作:
- 区间取相反数
- 区间赋 $0$
- 区间加
看着不太好维护。注意到区间取相反数本质上是区间 $\times -1$ ,区间赋 $0$ 本质上是区间 $\times 0$ ,直接写一个类似 P3373 的区间乘,区间加的线段树即可。
```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long lld;
const lld mdx=998244353;
const int maxn=2e5+900;
const int maxT=1e6+800;
int n,m,a[maxn],b[maxn],ind[maxn],nx=0;
int tl[maxT],tr[maxT];lld val[maxT],mul[maxT],add[maxT];
int getid(int x){return lower_bound(ind+1,ind+m+1,x)-ind;}
int ls(int x){return (tl[x]==tr[x]?0:2*x);}
int rs(int x){return (tl[x]==tr[x]?0:2*x+1);}
int len(int x){return tr[x]-tl[x]+1;}
lld srl(lld x,lld y){return (ind[y]-ind[x-1])%mdx;}
void pushup(int p)
{
val[p]=((ls(p)?val[ls(p)]:0)+(rs(p)?val[rs(p)]:0))%mdx;
}
void pushdown(int p)
{
lld pm=mul[p],pa=add[p];mul[p]=1,add[p]=0;
if(ls(p))
{
val[ls(p)]=(val[ls(p)]*pm+1ll*pa*srl(tl[ls(p)],tr[ls(p)]))%mdx;
add[ls(p)]=(add[ls(p)]*pm+pa)%mdx;
mul[ls(p)]*=pm;mul[ls(p)]%=mdx;
}
if(rs(p))
{
val[rs(p)]=(val[rs(p)]*pm+1ll*pa*srl(tl[rs(p)],tr[rs(p)]))%mdx;
add[rs(p)]=(add[rs(p)]*pm+pa)%mdx;
mul[rs(p)]*=pm;mul[rs(p)]%=mdx;
}
}
void build(int p,int l,int r)
{
tl[p]=l;tr[p]=r;mul[p]=1;add[p]=0;
if(l==r){val[p]=((l<=a[1]?1ll:0ll)*srl(l,l))%mdx;return ;}
int mid=(tl[p]+tr[p])/2;
if(l<=mid) build(2*p,l,mid);
if(mid<r) build(2*p+1,mid+1,r);
pushup(p);
}
void upd1(int p,int l,int r,int k)
{
if(l>r) return ;
if(l<=tl[p]&&tr[p]<=r)
{
val[p]*=k;val[p]%=mdx;
mul[p]*=k;mul[p]%=mdx;
add[p]*=k;add[p]%=mdx;
return ;
}
pushdown(p);int mid=(tl[p]+tr[p])/2;
if(l<=mid) upd1(ls(p),l,r,k);
if(mid<r) upd1(rs(p),l,r,k);
pushup(p);
}
void upd2(int p,int l,int r,int k)
{
if(l>r) return ;
if(l<=tl[p]&&tr[p]<=r)
{
val[p]+=1ll*(k*srl(l,r))%mdx;
val[p]%=mdx;
add[p]+=k;val[p]%=mdx;
return ;
}
pushdown(p);int mid=(tl[p]+tr[p])/2;
if(l<=mid&&ls(p)) upd2(ls(p),l,r,k);
if(mid<r&&rs(p)) upd2(rs(p),l,r,k);
pushup(p);
}
lld query(int p,int l,int r)
{
if(l<=tl[p]&&tr[p]<=r)
{
return val[p];
}
pushdown(p);lld ans=0;int mid=(tl[p]+tr[p])/2;
if(l<=mid&&ls(p)) ans+=query(ls(p),l,r);
if(mid<r&&rs(p)) ans+=query(rs(p),l,r);
return ans%mdx;
}
int main()
{
freopen("1591F.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) ind[i]=a[i];
sort(ind+1,ind+n+1);m=unique(ind+1,ind+n+1)-ind-1;
for(int i=1;i<=n;i++) a[i]=getid(a[i]);
build(1,1,m);
for(int i=2;i<=n;i++)
{
lld dlt=query(1,1,m);
upd1(1,1,m,-1);upd2(1,1,m,dlt);
upd1(1,a[i]+1,m,0);
}
lld A=query(1,1,m);A%=mdx;A=(A+mdx)%mdx;
printf("%lld\n",A);
return 0;
}
```