HDU - 4455 (DP)

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4455

XXX has an array of length n. XXX wants to know that, for a given w, what is the sum of the distinct elements’ number in all substrings of length w. For example, the array is { 1 1 2 3 4 4 5 } When w = 3, there are five substrings of length 3. They are (1,1,2),(1,2,3),(2,3,4),(3,4,4),(4,4,5) 
The distinct elements’ number of those five substrings are 2,3,3,2,2. 
So the sum of the distinct elements’ number should be 2+3+3+2+2 = 12
Input
There are several test cases. 
Each test case starts with a positive integer n, the array length. The next line consists of n integers a  1,a  2…a  n, representing the elements of the array. 
Then there is a line with an integer Q, the number of queries. At last Q lines follow, each contains one integer w, the substring length of query. The input data ends with n = 0 For all cases, 0<w<=n<=10  6, 0<=Q<=10  4, 0<= a  1,a  2…a  n <=10  6
Output
For each test case, your program should output exactly Q lines, the sum of the distinct number in all substrings of length w for each query.
Sample Input
7
1 1 2 3 4 4 5
3
1
2
3
0
Sample Output
7
10
12

题目大意:

求长度为w的连续区间中不同数字的个数的和。如题目example。

题解:

看到查询这么多应该考虑预处理一下。假设要求长度为len的区间的个数和。如果每次都暴力求解显然是不可能的。当len = 3时有(1,1,2)(1,2,3)(2,3,4)(3,4,4)(4,4,5),不妨把len-1的也列出来(因为暴力单独求解行不通,那就看看不同区间长度之间有什么联系吧)(1,1)(1,2)(2,3)(3,4)(4,4)(4,5)。我们可以发现长度为3的区间个数比长度为2的区间个数少一个,即数组最后的2个。加入长度为2的答案是X,长度为3的答案是Y,那么Y = X-(4,5)的不同的数字个数+(k=连续长度为3的有重复的数字的个数)。那么这个k是什么呢?先看一下数组a[i]={1,1,2,3,4,4,5}。记录每个数和自己相同且最近的距离。这样当我由长度2变为长度3的时候,这时候距离为2的就会在同一个区间中了,这时在剩下的距离数中减去距离为2的数就是还符合的距离数 = k(真的不知道该怎么表达了,具体看代码吧,结合由2到3的列式和数组中连续区间不能有重复的数字的想法应该就明白了)。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+100;
int a[maxn],vis[maxn],pos[maxn],f[maxn],sum[maxn];
LL dp[maxn];
int main(){
    int n;
    while(~scanf("%d",&n),n){
        memset(pos,0,sizeof(pos));
        memset(vis,0,sizeof(vis));
        memset(f,0,sizeof(f));
        memset(sum,0,sizeof(sum));
        for(int i = 1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i-pos[a[i]]]++;
            pos[a[i]] = i;
        }
        for(int i = 1;i<=n;i++){
            if(!vis[a[n-i+1]]){
                vis[a[n-i+1]] = 1;
                f[i] = f[i-1]+1;
            }
            else f[i] = f[i-1];
        }
        int ans = n;
        dp[1] = (LL)n;
        for(int i = 2;i<=n;i++){
            dp[i] = dp[i-1] - (LL)f[i-1];
            ans -= sum[i-1];
            dp[i] += (LL)ans;
        }
        int q ;
        scanf("%d",&q);
        while(q--){
            int p;
            scanf("%d",&p);
            printf("%lld\n",dp[p]);
        }
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值