hdu 4455 Substrings (经典DP)

Substrings

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1177    Accepted Submission(s): 356


Problem Description
XXX has an array of length n. XXX wants to know that, for a given w, what is the sum of the distinct elements’ number in all substrings of length w. For example, the array is { 1 1 2 3 4 4 5 } When w = 3, there are five substrings of length 3. They are (1,1,2),(1,2,3),(2,3,4),(3,4,4),(4,4,5)
The distinct elements’ number of those five substrings are 2,3,3,2,2.
So the sum of the distinct elements’ number should be 2+3+3+2+2 = 12
 

Input
There are several test cases.
Each test case starts with a positive integer n, the array length. The next line consists of n integers a 1,a 2…a n, representing the elements of the array.
Then there is a line with an integer Q, the number of queries. At last Q lines follow, each contains one integer w, the substring length of query. The input data ends with n = 0 For all cases, 0<w<=n<=10 6, 0<=Q<=10 4, 0<= a 1,a 2…a n <=10 6
 

Output
For each test case, your program should output exactly Q lines, the sum of the distinct number in all substrings of length w for each query.
 

Sample Input
   
   
   
   
7 1 1 2 3 4 4 5 3 1 2 3 0
 

Sample Output
   
   
   
   
7 10 12
 

Source
2012 Asia Hangzhou Regional Contest


题意:
给一个长度为n的数组,有q个询问w,输出区间长度为w的所有子序列中不同数的个数总和。


思路:
突破口是询问i与i+1有联系,所以要用DP解决该问题。
长度为i的区间变为长度为i+1的区间,变化为把长度为i的区间的最后一个区间删掉,然后从标号为i+1~n的元素依次添加到原来的第1~n-i的区间。
那么有  dp[i]=dp[i-1]-A+B;
A为最后i-1个元素的不同个数,这个好预处理,不多说了。
B则为标号为i+1~n的元素添到相应区间后看是否重复,不重复则+1。
B不太好处理,我们开一个数组s[i]记录所有元素中与其前面相等元素距离为i的有多少个,若一个元素前面没有相等的则这个元素即为其下标,对应的s[i]++。那么dp[i-1]到dp[i]递推时一个元素要加到i-1中的区间(即它前面的i-1个数中去),如果它不与前面i-1个数重复,那么就+1,从总体上来看就是加上s[i]~s[n]的累加和。把题目中的例子画几下就能知道其中的真理了。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
//#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 105
#define MAXN 1000005
#define mod 1000000007
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;

int n,m,ans;
int a[MAXN];
int last[MAXN],pre[MAXN]; // last[]-每个元素与其前面元素最近距离 pre[]-辅助last的数组
int num[MAXN];  // num[i]-i~n中有多少个不同元素
int s[MAXN];    // s[i]-与其前面相等元素距离为i的有多少个 若前面没有相等的则为下标i
int sum[MAXN];  // sum[i]-s[i]~s[n]的和
ll dp[MAXN];    // 答案数组

void presolve()
{
    int i,j;
    memset(pre,0,sizeof(pre));
    memset(s,0,sizeof(s));
    for(i=1;i<=n;i++)
    {
        if(pre[a[i]]) last[i]=i-pre[a[i]];
        else last[i]=i;
        pre[a[i]]=i;
        s[last[i]]++;
    }
    memset(pre,0,sizeof(pre));
    num[n+1]=0;
    for(i=n;i>=1;i--)
    {
        if(pre[a[i]]) num[i]=num[i+1];
        else pre[a[i]]=1,num[i]=num[i+1]+1;
    }
    sum[n+1]=0;
    for(i=n;i>=1;i--)
    {
        sum[i]=sum[i+1]+s[i];
    }
}
void solve()
{
    int i,j;
    dp[1]=n;
    for(i=2;i<=n;i++)
    {
        dp[i]=dp[i-1]-num[n-i+2]+sum[i];
    }
}
int main()
{
    int i,j,t;
    while(scanf("%d",&n),n)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        presolve();
        solve();
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d",&t);
            printf("%I64d\n",dp[t]);
        }
    }
    return 0;
}
/*
7
1 1 2 3 4 4 5
3
1
2
3

5
3 2 2 2 3
2
1
2
*/










 

你可能感兴趣的:(hdu 4455 Substrings (经典DP))