HDU 5289 Assignment(线段树)

Assignment

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 3361    Accepted Submission(s): 1563


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]
 
多校里的签到题。。。
题意:给出n个数,求n个数中满足区间内最大值最小值之差小于k的区间个数
分析:以每个数为区间的左端点l,找距离最远的右端点r(即区间长度最长)
    显然,有下面几条规律成立:
1.根据上述定义的l,r,对于任意的i∈[l,r]皆满足区间[l,i]内最大最小值差小于k,(即刚好第r+1个数比区间内任意数都小或大,或者r是最好一个数),故对于枚举的每一个区间左端点,满足条件的区间个数为r-l+1
2.当找到某个l的满足条件的最远的区间右端点后,枚举到下一个区间左端点的时候,一定有下一个区间的右端点大于等于上一个区间的右端点(这个好像是叫 尺取法,不过其实有点DP常识的人都很容易想到这个)
    于是,枚举每个数作为区间起点,找到其对应的最远右端点(即区间长度最长),ans += r-l+1即可
    找区间右端点的算法其实一看就是单调队列,不过单调队列敲的少,线段树天天敲,于是用的线段树(∩_∩)
code:
<pre name="code" class="cpp">#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 100000;
struct Node
{
    int l,r,mn,mx;
}q[4*N];
struct node
{
    int mx,mn;
};
void build(int i,int l,int r)
{
    q[i].l = l,q[i].r = r;
    if (l == r)
    {
        scanf("%d",&q[i].mx);
        q[i].mn = q[i].mx;
        return;
    }
    int mid = (l+r)/2;
    build(i<<1,l,mid);
    build((i<<1)+1,mid+1,r);
    q[i].mx = max(q[i<<1].mx,q[(i<<1)+1].mx);
    q[i].mn = min(q[i<<1].mn,q[(i<<1)+1].mn);
}
node query(int i,int l,int r)//询问的话,我是返回结构体,使得最大值最小值同时都被返回了
{
    if (l<=q[i].l&&q[i].r<=r)
    {
        node ans ;
        ans.mx = q[i].mx,ans.mn = q[i].mn;
        return ans;
    }
    int mid = (q[i].l+q[i].r)/2;
    if (r<=mid) return query(i<<1,l,r);
    else if (l > mid) return query((i<<1)+1,l,r);
    else
    {
        node ans;
        node x = query((i<<1),l,mid);//这个地方稍稍变动,其他就是裸的线段树且无更新情况
        node y = query((i<<1)+1,mid+1,r);
        ans.mx = max(x.mx,y.mx),ans.mn = min(x.mn,y.mn);
        return ans;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int n,k;
        scanf("%d %d",&n,&k);
        build(1,1,n);
        ll sun = 1;//以最后一个数为左端点时满足条件的区间个数为1,后面枚举就是从1到n-1
        int last = 1,j;//j记录最长区间的右端点,last记录上一次区间的右端点
        for (int i = 1;i < n;++i)
        {
            for (j = max(i,last);j <= n;++j)
            {
                node t = query(1,i,j);
                if (t.mx - t.mn>=k)
                {
                    break;//这里的j是在不满足的位置,于是后面会有一句--j(不经历此break的情况也不会出错)
                }

            }
            --j;
            sun += j - i+1;
            last = min(j,n);
        }
        printf("%lld\n",sun);
    }
    return 0;
}


 
  

你可能感兴趣的:(HDU 5289 Assignment(线段树))