o_o 当前时间是:

4:02:23 AM

 

ARC125D 题解

ARC125D

题意

给定长度为 n 的序列中,求其中只出现过一次的非空子序列的个数,对 998244353 取模。

题解

不难发现,一个只出现过一次的子序列合法的充分必要条件是:

  1. 头部元素 ai 是原序列中下标最小的(即最左边的)值为 ai 的元素
  2. 由对称性,该子序列最后一个元素 ai 是原序列中最后一个出现的值为 ai 的元素
  3. 子序列中任意相邻两元素在原序列中,不妨设它们的下标分别是 l,rai,l<i<raialaiar

这些条件可以举一些例子验证。

为了简化程序,我们可以为原序列设首尾两个哨兵,

具体地,令 a0=max{ai}+1,an+1=max{ai}+2,1in

这样 a0an+1 就和原序列任意元素不相同了,

接着,我们求以 a0 为头,an+1 为尾的合法子序列个数,

我们就不必考虑条件 1 和条件 2 了。

然后就可以 dp 了,

fi 为以 ai 结尾的合法子序列个数,lsti 为值为 i 的元素目前为止最后出现的位置,则

  • f0=1

  • fi=j=lstaii1fj×[j==lstaj](i0)

实际我们需要不断维护 lstai 每次顺带把 flstai (更新前的) 置为 0 ,这样转移时的求和就是区间求和了。

再加上单点修改,可以直接树状数组维护。

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N=200010;
typedef long long ll;
const int mod=998244353;
int a[N],n,c[N],f[N],lst[N],m;
inline int lowbit(int x){return x&-x;}
void add(int x,int val){
x++; //由于要用到0这个下标,树状数组中把所有下标偏移1
for(;x<=n+1;x+=lowbit(x)) c[x]=((c[x]+val)%mod+mod)%mod;
}
int ask(int x){
x++; //同上
int ret=0;
for(;x;x-=lowbit(x)) ret=((ret+c[x])%mod+mod)%mod;
return ret;
}
int main(){
cin>>n;
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
m=max(m,a[i]);
}
a[0]=m+1,a[n+1]=m+2; //设哨兵
f[0]=1; add(0,1);
for(int i=1; i<=n+1; i++){
f[i]=(ask(i-1)-ask(lst[a[i]]-1)+mod)%mod;
if(lst[a[i]]) add(lst[a[i]],-f[lst[a[i]]]);
add(i,f[i]);
lst[a[i]]=i;
}
cout<<(f[n+1]-1+mod)%mod; //子序列不能空,所以减1
return 0;
}
posted @   zzafanti  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示