CF1591F
退役选手打了打 CF 玩,发现脑子不灵了。
考虑一个暴力 DP ,设 表示处理到第 个, 的方案数,则转移方程为:
首先有一个关键结论:把 数组排序,在 区间的答案全部是相同的。因此,有用的 其实只有 个。可以把 的一维搬到线段树上,具体来说,以 为下标 为权值开一棵线段树。从 转移到 直接在线段树上修改。
注意对于同一个 的所有 来说, 都是一个定值,于是这个线段树只用支持三种操作:
- 区间取相反数
- 区间赋
- 区间加
看着不太好维护。注意到区间取相反数本质上是区间 ,区间赋 本质上是区间 ,直接写一个类似 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;
}
```
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界