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 }
View Code

 

posted @ 2018-10-14 20:51  cellur925&Chemist  阅读(264)  评论(0编辑  收藏  举报