题意:给出n个数字的序列,然后q次询问,长度为w的子串的不同数字个数之和。
题解:f[i]表示长度为w的子串不同数字个数之和,状态转移方程f[i] = f[i - 1] - 最后i-1个不同数字个数 + 每组从i-1到i多加的数字是否与前i-1个数字重复,那么最后i-1个不同数字个数循环一遍就能得到,然后用树状数组维护每个数字左边长度为i的区间内出现和自己相同的数字的个数之和。思路出自
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1000005;
int n, q, m, a[N], pos[N];
ll C[N], f[N], last[N];
int lowbit(int x) {
return x & (-x);
}
void modify(int x, int d) {
while (x <= n) {
C[x] += d;
x += lowbit(x);
}
}
ll query(int x) {
ll ret = 0;
while (x > 0) {
ret += C[x];
x -= lowbit(x);
}
return ret;
}
int main() {
while (scanf("%d", &n) == 1 && n) {
memset(C, 0, sizeof(C));
memset(pos, -1, sizeof(pos));
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (pos[a[i]] != -1) {
modify(i - pos[a[i]] + 1, 1);
modify(i + 1, -1);
}
pos[a[i]] = i;
}
memset(pos, -1, sizeof(pos));
pos[a[n]] = n;
last[1] = 1;
for (int i = 2, j = n - 1; i <= n; i++, j--) {
last[i] = last[i - 1];
if (pos[a[j]] == -1)
last[i]++;
pos[a[j]] = j;
}
f[1] = n;
for (int i = 2; i <= n; i++)
f[i] = f[i - 1] - last[i - 1] + n - i + 1 - query(i);
scanf("%d", &q);
while (q--) {
scanf("%d", &m);
printf("%lld\n", f[m]);
}
}
return 0;
}