• 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
  • 相关阅读:
    Linux Shell for循环写法总结 皇星客栈
    关于adr指令的理解 皇星客栈
    lds linux下的通用链接脚本 皇星客栈
    2430实验点对点通信实验 皇星客栈
    #pragma vector 皇星客栈
    linux下firefox安装Adobe Flash Player插件 皇星客栈
    一个shell脚本引发的对于分号(;)和$#的使用说明(转载) 皇星客栈
    代码打开wince自带的wif配置窗口i
    C#数组的合并拆分
    Coding4Fun
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9787033.html
Copyright © 2020-2023  润新知