1.题目描述:点击打开链接
2.解题思路:本题利用dp解决。不过这个dp的思路的确比太容易想到。需要观察规律才能发现。我们可以从贡献值的角度考虑。以题目中给的样例来说明这种方法。
通过观察相邻两个w值,我们会发现一个事实:每个大区间都包含了小区间的解(这里的解即原题中的sum值)。但是这还不够,观察图上标记为红色的数字,它们可以看做是上一个w值对应的区间多出来的新数字,如果我们可以算出这些红色数字的贡献值,那么我们就能得到当前w下的解。那么该如何计算呢?
继续观察后会发现,每次w增加1,区间个数就会减少1个,也就是说,w-1时候的最后一个区间的结果要被减掉,才是w-1的解对当前w的贡献。接下来看红色数字的贡献,由于当前区间长度为w,那么某个数字i和前一个i的距离差<w时,这些数字都不能算入。比如以w=5的情况为例,w=5时候,第一个红色数字4要被算入,因为它和前一个4(如果不存在前一个数就看做在0位置,数组的起始下标从1开始)距离为5,不满足距离<w,因此要被算入,而第二个红色数字4和前一个4距离为1,满足1<w的要求,因此不能被算入。也就是说,我们需要知道数字的距离分别有几个,比如距离为x的有dis[x]个,那么w-1时候的红色数字的贡献值假设为sum,那么w时候,红色数字的贡献就是sum-dis[w-1]。同时,我们用last[i]表示最后i个数字有几种不同的数字。于是得到如下的递推式:
dp(w)=dp(w-1)-last(w-1)+(sum-dis[w-1]);
上式中的sum是w-1时候红色数字对答案的贡献。当w=0时候,sum=n。这样就可以用O(1)时间得到所有的dp(w)了。当然,根据上述分析,我们需要提前预处理出来dis[i]和last[i],详细过程见代码。
本题最终的时间复杂度是O(N+Q)。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;
const int N = 1000010;
int a[N], dis[N], vis[N], last[N], sum;
ll dp[N];
int n;
int main()
{
while (~scanf("%d", &n) && n)
{
sum = n; //sum是前一个w值中红色数字对答案的贡献值
me(vis); me(dis);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
dis[i - vis[a[i]]]++; //统计不同距离的个数
vis[a[i]] = i;//更新a[i]上一次出现的位置
}
me(vis);
last[1] = 1;//last[i]表示最后i个数中有几个不同的数
vis[a[n]] = 1;//标记a[n]访问过
for (int i = 2; i <= n; i++)
if (!vis[a[n - i + 1]])//发现新的没有出现过的数,last[i]++
{
vis[a[n - i + 1]]++;
last[i] = last[i - 1] + 1;
}
else last[i] = last[i - 1];
dp[1] = n;
for (int i = 2; i <= n; i++)
{
sum -= dis[i - 1];//更新sum,得到当前w时候红色数字对答案的贡献值
dp[i] = dp[i - 1] - last[i - 1] + sum;
}
int q;
scanf("%d", &q);
for (int i = 0; i<q; i++)
{
int w;
scanf("%d", &w);
printf("%I64d\n", dp[w]);
}
}
}