7.23 测试 递推式

题目描述

给定序列 A, 求出 A 中本质不同的子序列 (包含空的子序列) 个数模
10 9 + 7 的结果.
一个序列 B 是 A 的子序列需要满足 A 删掉某些元素后能够得到 B.
两个子序列中对应位置的数都相同我们就认为这两个子序列本质相同.

输入格式

第一行包含一个整数 N , 代表序列的长度.
接下来一行 N 个整数, 第 i 个数代表 A i .

输出格式

1.3
输出一个整数代表答案.

样例输入

5
2 3 1 3 2

样例输出

27
数据范围
对于 20% 的数据, N ≤ 10
对于 40% 的数据, N ≤ 20
对于 70% 的数据, N ≤ 100000, 1 ≤ A i ≤ 100
2对于 100% 的数据, N ≤ 1000000, 1 ≤ A i ≤ N

题目分析

首先我们可以用暴力搜索(实在想不出正解的话)我们可以考虑当前第i位,如果这一位的数字是前面从未出现过的,那我们就直接将f[i-1]*2+1即可。(1是指这个元素单独可以为一个子集)如果前面出现过这个数字,那我们就将答案赋值为(f[i-1]*2-f[pos[a[i]]-1])在这里不加一是因为前面出现过,减去f[pos[a[i]]-1]是指在pos[a[i]]-1前面的所有数字分别与pos[a[i]]这个位置上的数都组合过,如果再与当前i位上这个数字组合,就会重复如样例中的2 3 1 3 2中第二个3和第一个三分别与第一个2组合为一种答案(pos为这个数字最近出现的一次位置)

程序代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a[1001000],vis[2000000],ans;
int n,pos[1001000],f[1001000];
const int inf=1e9+7;
inline int read(){
    int x=0,w=1;char ch;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
    return x*w;
}
int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    n=read();
    for(register int i=1;i<=n;++i) a[i]=read();
    f[1]=1;f[0]=0;vis[a[1]]=true;
    for(register int i=2;i<=n;++i) {
        if(!vis[a[i]]) {
            f[i]=(f[i-1]*2+1)%inf;
            pos[a[i]]=i;
            vis[a[i]]=true;
        }
        else {  
            f[i]=((f[i-1]*2%inf-f[pos[a[i]]-1])+inf)%inf;
            //注意在这里要加一个inf,避免因为减的时候出现负值而导致答案错误
            pos[a[i]]=i;
        }
    }
    printf("%d",f[n]+1);    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值