Loading

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

posted @ 2021-12-19 15:08  SS80194  阅读(69)  评论(0)    收藏  举报