题目描述
给定序列 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;
}