CF1591F

退役选手打了打 CF 玩,发现脑子不灵了。

考虑一个暴力 DP ,设 fi,j 表示处理到第 i 个, bi=j 的方案数,则转移方程为:
fi,j={j=1bi1fi1,jfi1,j(jbi)0(j>bi)

首先有一个关键结论:把 a 数组排序,在 (ai,ai+1] 区间的答案全部是相同的。因此,有用的 j 其实只有 O(n) 个。可以把 j 的一维搬到线段树上,具体来说,以 j 为下标 fi,j 为权值开一棵线段树。从 i 转移到 i+1 直接在线段树上修改。

注意对于同一个 i 的所有 j 来说, j=1bi1fi1,j 都是一个定值,于是这个线段树只用支持三种操作:
- 区间取相反数
- 区间赋 0
- 区间加

看着不太好维护。注意到区间取相反数本质上是区间 ×1 ,区间赋 0 本质上是区间 ×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 @   SS80194  阅读(61)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界
点击右上角即可分享
微信分享提示
主题色彩