2015多校赛1B-HDU5289Assignment

Problem Description
Tom owns a company and he is the boss. There are n staffs which are numbered from 1 to n in this company, and every staff has a ability. Now, Tom is going to assign a special task to some staffs who were in the same group. In a group, the difference of the ability of any two staff is less than k, and their numbers are continuous. Tom want to know the number of groups like this.
 

Input
In the first line a number T indicates the number of test cases. Then for each case the first line contain 2 numbers n, k (1<=n<=100000, 0<k<=10^9),indicate the company has n persons, k means the maximum difference between abilities of staff in a group is less than k. The second line contains n integers:a[1],a[2],…,a[n](0<=a[i]<=10^9),indicate the i-th staff’s ability.
 

Output
For each test,output the number of groups.
 

Sample Input
  
  
2 4 2 3 1 2 4 10 5 0 3 4 5 2 1 6 7 8 9
 

Sample Output
  
  
5 28
Hint
First Sample, the satisfied groups include:[1,1]、[2,2]、[3,3]、[4,4] 、[2,3]
 

题目如上,大意是求有多少个连续区间使得区间最小值与最大值之间相差不超过K,最后输出这样的区间个数。

一开始自己想的比较挫,笑着用线段树logN查询区间最大值最小值,然后N方的一个枚举起点终点,这样肯定超时,我就借鉴了下尺取法的思想。对后一个枚举优化了一下,然后莫名其妙就过啦,可能数据强度一般吧。

下面来说比较好的题目思路

枚举起点,设为i,然后在后面找到能是a[i]满足条件的最右边的坐标,假设为j,那么区间[i,i][i.i+1].....[i,j]一定都是符合条件的,那么起点为i对答案的贡献就是j-i+1,那么我们对每个数找到维护这么一个区间就行啦。下面就是具体的编程思路:

对起点从0到n枚举,二分终点即可。但是如果求区间最大值最小值的复杂度是多少那?如果是用分治或线段树可以在lonN的复杂度下完成,这样复杂度是NlogNlogN,应该也能过(我并没有试过)。但其实有一种ST算法可以NlogN的预处理。O(1)的回答区间最大最小值。这样就可以使复杂度变为NlogN啦


下面是具体的代码实现:

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <cstring>
#include <map>
#include <set>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int N=100010;
int a[N];
int sum[N];
int Min[N][20];
int Max[N][20];
int getlog(int x){
    int i=0;
    while(x!=0){
        x/=2;
        i++;
    }
    return i-1;
}

void init(int n){
    for(int i=0;i<n;i++)
        Max[i][0]=Min[i][0]=a[i];
    for(int j=1;j<20;j++){
        for(int i=0;i<n;i++){
            if(i+(1<<j)-1<n){
                Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
                Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);
            }
        }
    }
}
int get(int l,int r){
    int len=(r-l+1);
    len=getlog(len);
    return max(Max[l][len],Max[r+1-(1<<len)][len])-min(Min[l][len],Min[r+1-(1<<len)][len]);
}
int binary(int i,int n,int k){
    int low=i,high=n;
    while(low<=high){
        int mid=(low+high)>>1;
        int t=get(i,mid);
        if(t>=k)
            high=mid-1;
        else
            low=mid+1;
    }
    return high;
}
int main(){
    int T;
    long long ans;
    scanf("%d",&T);
    while(T--){
        int n,k;
        ans=0;
        scanf("%d%d",&n,&k);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        init(n);
        for(int i=0;i<n;i++)
            sum[i]=binary(i,n-1,k);
        for(int i=0;i<n;i++){
            ans+=(sum[i]-i+1);
        }
        printf("%lld\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值