这道题思路十分巧妙,我觉得看到10^6次方的数据和查询,一般都会先往线段树方向考虑。我也想了半天,实在思考不出来去看了邝大神的博客才恍然大悟。编程实现不难主要是思路很难想到。时间复杂度为O(n),dp方程:dp[i] = dp[i-1]+A-B (A,B含义见注释)。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxn 1000100 5 #define ll long long 6 using namespace std; 7 8 int A[maxn], B[maxn], C[maxn], num[maxn], flag[maxn]; 9 int n, q; 10 ll dp[maxn]; 11 12 void init() 13 { 14 //初始化A数组表示最后i-1个有多少个不重复的元素 15 memset(flag, 0, sizeof flag); 16 A[1] = 0; 17 for(int i=2; i<=n; i++){ 18 A[i] = A[i-1]; 19 if(flag[num[n-i+1]]==0){ 20 flag[num[n-i+1]] = 1; 21 A[i] ++; 22 } 23 } 24 //初始化B数组表示最近的相同元素相隔的距离为i的个数 25 memset(flag, -1, sizeof flag); 26 memset(B, 0, sizeof B); 27 for(int i=0; i<n; i++){ 28 if(flag[num[i]]==-1){ 29 flag[num[i]] = i; 30 B[i+1]++; 31 } 32 else{ 33 B[i-flag[num[i]]] ++; 34 flag[num[i]] = i; 35 } 36 } 37 } 38 39 void solve() 40 { 41 init(); 42 int sum; 43 dp[1] = sum = n; 44 for(int i=2; i<=n; i++){ 45 sum-=B[i-1]; 46 dp[i] = dp[i-1]-A[i]+sum; 47 } 48 } 49 50 int main() 51 { 52 // freopen("in.txt", "r", stdin); 53 54 int tq; 55 while(scanf("%d", &n)!=EOF && n) 56 { 57 for(int i=0; i<n; i++){ 58 scanf("%d", &num[i]); 59 } 60 scanf("%d", &q); 61 solve(); 62 for(int i=0; i<q; i++){ 63 scanf("%d", &tq); 64 printf("%I64d ", dp[tq]); 65 } 66 } 67 return 0; 68 }