1. 程式人生 > >51Nod 1202 子序列個數(簡單計數dp)

51Nod 1202 子序列個數(簡單計數dp)

 收藏  關注 子序列的定義:對於一個序列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,有些子序列可能是相同的,這裡只算做1個,請輸出a的不同子序列的數量。由於答案比較大,輸出Mod 10^9 + 7的結果即可。 Input
第1行:一個數N,表示序列的長度(1 <= N <= 100000)
第2 - N + 1行:序列中的元素(1 <= a[i] <= 100000)
Output
輸出a的不同子序列的數量Mod 10^9 + 7。
Input示例
4
1
2
3
2
Output示例
13

題解:我們知道如果不存在重複的數,那麼dp[i]=dp[i-1]*2含空集的情況)。現在考慮出現了重複的數。比如當前要取的數為a[i],且a[i]最近一次在之前的j位置出現過了。那麼有dp[i]=dp[i-1]*2-dp[j-1]所以我們利用一個數組mark記錄下a[i]出現的位置就好了,沒有出現過為0。

注意:在運算中會出現減的情況,所以取模是要加上mod

程式碼如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 100010
#define mod 1000000007
long long dp[maxn];
int a[maxn];
int mark[maxn];//利用hash記錄a[i]前面是否出現了一次,並記錄下上一次出現的位置 
int main()
{
	int n,i,j;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=1;i<=n;++i)
			scanf("%d",&a[i]);
		memset(dp,0,sizeof(dp));
		memset(mark,0,sizeof(mark));
		dp[0]=1;
		for(i=1;i<=n;++i)
		{
			if(mark[a[i]]==0)
				dp[i]=(dp[i-1]*2)%mod;
			else
				dp[i]=(2*dp[i-1]-dp[mark[a[i]]-1]+mod)%mod;
			mark[a[i]]=i;
		}
		printf("%lld\n",dp[n]-1);
	}
	return 0; 
}