【题解】子序列个数 [51nod1202] [FZU2129]

【题解】子序列个数 [51nod1202] [FZU2129]

传送门:子序列个数 \([51nod1202]\) \([FZU2129]\)

【题目描述】

对于给出长度为 \(n\) 的一个序列 \(a\),求出不同的非空子序列个数。答案对 \(10^9+7\) 取模。

【样例】

样例输入:
4
1
2
3
2

样例输出:
13

【数据范围】

\(100\%\) \(1 \leqslant N,a[i] \leqslant 10^5\)


【分析】

先考虑没有相同整数的情况,每个元素有选或不选两种情况,一共 \(n\) 个元素,又不能有空集,答案为 \(2^n-1\)
如果要写递推方程的话就是 \(dp[i]=dp[i-1]*2+1\),其中 \(dp[i]\) 表示选择原序列中前 \(i\) 个元素所能构成的不同子序列数量,\(dp\) 方程含义为:在长度为 \(i-1\) 的序列中是否加入一个 \(a[i]\) \((\) \(2*dp[i-1]\)\()\) 以及只选 \(a[i]\) 的情况 \((\) \(1\)\()\)

假设就按照这个方程推下去,如果前面某一位 \(j\) 上的数与 \(a[i]\) 相同,会对 \(dp[i]\) 造成什么影响呢?

首先,只选 \(a[i]\) 这一个数的情况已经在前面统计过了。
然后,\(j\) 前面的所有方案都不能转移到 \(dp[i]\) 上面来,因为它们已经转移到 \(dp[j]\) 上。

这里的 \(“\) 所有方案 \(”\) 其实就是 \(dp[j-1]\),且 \(j\) 必须为最接近 \(i\) 的那一个位置,否则就不能代表所有被算重复的情况

\(w[a[i]]\) 表示前面等于 \(a[i]\) 的且位置最靠近 \(i\) 的数的位置,若不存在则 \(w[a[i]]=0\)\(dp\) 方程为:

\(dp[i]=\begin{cases} dp[i-1]*2+1 & w[a[i]]=0\\dp[i-1]*2+1-dp[w[a[i]]-1]-1 & w[a[i]]!=0 \end{cases}\)

时间复杂度为:\(O(n)\)


【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
const int N=1e6+3,P=1e9+7;
int n,ans,a[N],W[N],dp[N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
int main(){
    while(scanf("%d",&n)!=EOF){
    	memset(dp,0,sizeof(dp));
    	memset(W,0,sizeof(W));
    	for(Re i=1;i<=n;++i){
            in(a[i]);
            dp[i]=((dp[i-1]<<1)%P+1)%P;
            if(W[a[i]])((dp[i]-=(dp[W[a[i]]-1]+1)%P)+=P)%=P;
            W[a[i]]=i;
    	}
    	printf("%d\n",dp[n]%P);
    }
}
posted @ 2019-08-15 17:12  辰星凌  阅读(408)  评论(0编辑  收藏  举报