HDU 4455(dp)

题意:给定一个序列ai,个数为n。再给出一系列w;对于每个w,求序列中,所有长度为w的连续子串中的权值和,子串权值为子串中不同数的个数。

思路:动态规划,用dp[w]表示当前长度为w的时候的权值和。显然dp[1] = n; 如果求dp[2]的话,那么它可以由dp[1]推出来,首先它比dp[1]少了最后一个子序列,那么最后一个子序列的权值用num来表示,num[i]从后面开始数i位的权值,也就是不同的个数。然后在计算每个子序列多了一个多元素多增加的权值,这里用sum[i]表示长度两个相同元素最近距离大于等于i的总个数,关键是这个怎么求,那么我们还可以开一个辅助数组c, c[i]表示数组中两个相同的数最小距离为i的总个数。那么sum[i] = sum[i + 1] + c[i], 所以dp[i] = dp[i - 1] + sum[i] - num[i - 1];

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1000100;
typedef long long ll;
ll dp[maxn];
int a[maxn];
int c[maxn];
int sum[maxn];
int num[maxn];
int pre[maxn];
int main()
{
    int n, q;
    while (~scanf("%d", &n) && n)
    {
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        memset(c, 0, sizeof(c));
        memset(pre, 0, sizeof(pre));
        for (int i = 1; i <= n; i++)
        {
            c[i - pre[a[i]]]++;
            pre[a[i]] = i;
        }
        sum[n] = c[n];
        for (int i = n - 1; i >= 1; i--)
            sum[i] = sum[i + 1] + c[i];
        memset(c, 0, sizeof(c));
        c[a[n]] = 1;
        num[1] = 1;
        for (int i = 2; i <= n; i++)
        {
            if (c[a[n - i + 1]])
                num[i] = num[i - 1];
            else
            {
                num[i] = num[i - 1] + 1;
                c[a[n - i + 1]] = 1;
            }
        }
        dp[1] = n;
        for (int i = 2; i <= n; i++)
            dp[i] = dp[i - 1] + sum[i] - num[i - 1];
        scanf("%d", &q);
        int t;
        while (q--)
        {
            scanf("%d", &t);
            printf("%lld\n", dp[t]);
        }
    }
    return 0;
}

 

posted @ 2015-10-23 10:55  Howe_Young  阅读(510)  评论(0编辑  收藏  举报