子序列个数

来源:互联网 发布:舞蹈教学软件 编辑:程序博客网 时间:2024/06/14 13:53

子序列个数
Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
Submit Status Practice FZU 2129

Description

子序列的定义:对于一个序列a=a[1],a[2],......a[n]。则非空序列

a'=a[p1],a[p2]......a[pm]为a的一个子序列,其中1<=p1<p2<.....<pm<=n。

例如4,14,2,3和14,1,2,3都为4,13,14,1,2,3的子序列。

对于给出序列a,请输出不同的子序列的个数。

(由于答案比较大,请将答案mod 1000000007)

Input

输入包含多组数据。每组数据第一行为一个整数n(1<=n<=1,000,000),

表示序列元素的个数。

第二行包含n个整数a[i] (0<=a[i]<=1,000,000)表示序列中每个元素。

Output

输出一个整数占一行,为所求的不同子序列的个数。由于答案比较大,
请将答案mod 1000000007。

Sample Input

4
1 2 3 2

Sample Output

13

Hint

其中40%数据点1<=n<=1000。

算法分析:

设 f(k)为k长度的序列的子序列个数,那么很显然有以下推论:

  • f(k)的个数包括f(k-1)的个数,因为f(k-1)的每一个都是f(k)的子序列,然后把f(k-1)的每个序列和a[k]组合起来,这些序列也是f(k)的子序列,个数还是f(k-1),载加上单独的a[k],那么

f(k)=2*f(k-1)+1

  • 上面这个表达式是当a[k]和前面的数都不同的时候的情况,如果a[k]在前面出现过的话,那f(k)的个数除了上面那些的话:

    • 还需要减去最近一次出现a[k]这个数值的地方-1的子序列个数,因为这些算重复了
    • +1也没有了,因为f(a[k]上次出现的位置)包括了a[k]单独算一次的情况

f(k)=2*f(k-1)-f(a[k]上次出现的位置-1)

有了这两个表达式,就是一个完整的递推关系了,a[k]上次出现的位置的保存,可以用一个hash表来存储,这样速度很快,但是题目说a[k]的范围是0到220,那可以用一个220的数组来存储,反正也不会溢出,省得用hash了。

代码:

#include <cstdio>#include <cstring>#include <iostream>using namespace std;const int maxn = 1e6 + 5;const int mod = 1000000007;long long f[maxn];        //避免超intint A[maxn], ls[maxn];int main(){    int n;    while(~scanf("%d", &n)) {              memset(ls, 0, sizeof(ls));        for(int i = 1; i <= n; i++) {        scanf("%d", &A[i]);        }        for(int i = 1; i <= n; i++) {            if(ls[A[i]] == 0) f[i] = (2 * f[i - 1] + 1) % mod;            else f[i] = (2 * f[i - 1] - f[ls[A[i]] - 1] + mod) % mod;            ls[A[i]] = i;        }        printf("%lld\n", f[n]);    }    return 0;}


0 0
原创粉丝点击