题意:给定一个序列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; }
时间: 2024-10-22 12:31:37