LuoguP1370 Charlie的云笔记序列 【dp】By cellur925
题目大意:给你一个序列,求出它所有区间的本质不同的子序列个数。(空序列也算作本质不同),数据范围$1e5$。
我们肯定是不能一个个枚举区间的...而且这个复杂度下,也就大概$O(n)$或$O(nlogn)$了...
然后...这是个计数类的dp。我们先尝试都搞上,然后再去重。
设$f[i]$表示$i$到$n$(即后缀)所有可能的子序列的个数和。
那么边界有$f[n]=2$。(最后一个元素+空序列),每次从$i+1$转移过来。
首先肯定有$f[i]=f[i+1]*2+2$。就是在$(i+1,i+1),(i+1,i+2),(i+1,i+3)...(i+1,n)$的前面加上或不加$ai$。再加上$(i,i)=2$。
之后考虑去重。如果$ai=aj$,那么对于很多子序列都是会有重复的,于是我们需要减去$f[j]+1$。(+1是$i$自身)
而对于如何找$j$的位置,我们可以用一个$nxt$数组来记录离当前最近的$a[i]$出现的位置。每次更新。因为$a[i]$范围到了$1e8$,考虑离散化。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #define maxn 100090 4 5 using namespace std; 6 typedef long long ll; 7 const ll moder=998244353; 8 9 int n,cnt; 10 int a[maxn],b[maxn],nxt[maxn]; 11 ll ans,f[maxn]; 12 13 int main() 14 { 15 scanf("%d",&n); 16 for(int i=1;i<=n;i++) 17 scanf("%d",&a[i]),b[i]=a[i]; 18 sort(b+1,b+1+n); 19 int cnt=unique(b+1,b+1+n)-(b+1); 20 for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b; 21 f[n]=2; 22 nxt[a[n]]=n; 23 for(int i=n-1;i>=1;i--) 24 { 25 (f[i]=f[i+1]*2+2)%=moder; 26 if(nxt[a[i]]) 27 { 28 int pos=nxt[a[i]]; 29 f[i]=(f[i]-f[pos+1]-1+moder)%moder; 30 } 31 nxt[a[i]]=i; 32 } 33 for(int i=1;i<=n;i++) 34 (ans+=f[i])%=moder; 35 printf("%lld",ans%moder); 36 return 0; 37 }
独立意志与自由思想是必须争的,且须以生死力争。