您的位置:首页 > 其它

HDU 4455(dp)

2015-10-23 10:55 302 查看
题意:给定一个序列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
= c
;
for (int i = n - 1; i >= 1; i--)
sum[i] = sum[i + 1] + c[i];
memset(c, 0, sizeof(c));
c[a
] = 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: