hdu 5289(单调队列)

Assignment

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


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]
 

 

Author
FZUACM
 

 

Source
 

 

Recommend
We have carefully selected several similar problems for you:   5309  5308  5307  5306  5305 
题目描述:
求一段序列中有多少组这样的子区间,子区间满足这样的条件:最大值与最小值之差不超过k,hdu3415的意思为求长度小于等于k的子序列的最大值。
这道题有一个关键的思想是:小区间的最大值与最小值之差一定小于等于包含它的大区间的最大值与最小值之差,也就是说区间的最大值与最小值之差
是单调的,区间越大,差值越大,当然,大区间是覆盖小区间的,
所以我们可以用RMQ或者线段树或者树状数组来查询一个子区间的最大值与最小值,然后枚举起点,二分查找满足条件的终点,或者使用线段树来查找
[i,n]这个区间的终点,如果这个区间满足,那么答案就为n,如果不满足,查左节点,如果不满足继续往下查找,如果满足,说明不满足的点在右节点,
查找右节点。
当然也可用单调队列,枚举终点,i从1到n,数组下标从0开始,初始front=1,[front,i]区间由单调队列给出最大值与最小值,这里使用两个
单调队列,如果[front,i]区间内最大值与最小值差值大于等于k,front++,当然如果两个单调队列的头指针如果等于front,也需要++,每次[front,i]区间内
的答案就为i-front+1,front不必要每次重新初始化为0,因为[front,i-1]本来满足条件,说明[k,i](k<front)这个区间内肯定不满足,小区间都不满足了,说明
[k,i](k<front)也肯定不满足。
#include <iostream>

#include <cstdio>

#include <cmath>

#include <cstdlib>

#define LL long long

#define maxn 110000

using namespace std;

LL  k;

int n;

LL a[maxn];

int up[maxn];

int down[maxn];

int Head1,Tail1;

int Head2,Tail2;

int Front;

void solve()

{

   LL ans=1;

    Head1=0; Tail1=0;

    Head2=0; Tail2=0;

    up[Tail1++]=0;

    down[Tail2++]=0;

    Front=0;

    for(int i=1;i<n;i++)

    {

        while(Head1<Tail1 && a[up[Tail1-1]]>a[i])

            Tail1--;

        up[Tail1++]=i; //存入决策,序号

        while(Head2<Tail2 && a[down[Tail2-1]]<a[i])

            Tail2--;

        down[Tail2++]=i;

        while(a[down[Head2]]-a[up[Head1]]>=k)

        {

           if(up[Head1]==Front)    Head1++; //删掉队首

           if(down[Head2]==Front)    Head2++;

             Front++;

        }

        ans+=i-Front+1;

       // printf("%d ",i-Front+1);

    }

    printf("%lld\n",ans);

}

int main()

{

    int t;

    scanf("%d",&t);

    while(t--)

    {

        scanf("%d%lld",&n,&k);

        for(int i=0;i<n;i++)

        {

             scanf("%lld",&a[i]);

        }

        solve();

    }

    return 0;

}

 

由于区间越大,差值越大,所以可以枚举起点,二分查找终点的坐标,ans加上(R-i)+1即可。
#include <iostream>

#include <cstdio>

#include <cmath>

#include <cstdlib>

#define LL long long

#define maxn 110000

#include <cstring>

using namespace std;

LL  k;

int n;

LL a[maxn];

LL  d[maxn][35]; //d数组代表最小值,以i开始,长度为2^j

LL  D[maxn][35];  //d数组代表最大值

void init()

{

    memset(d,0,sizeof(d));

}

void RMQ_init()

{

    for(int j=1; (1<<j) <=n ;j++) //枚举长度

   {

       for(int i=0;(i+ (1<<j))-1<n;i++)  //(1<<j)为长度

     {

           d[i][j]= min(d[i][j-1],d[i+ (1<<(j-1)) ][j-1]);

           D[i][j]= max(D[i][j-1],D[i+ (1<<(j-1))][j-1]);

           //printf("%d %d %lld %lld\n",i,j,d[i][j],D[i][j]);

          // cout<<endl;

       }

   }

}

LL q(int L,int R)

{

    int k=0;

    while((1<<(k+1)) <= (R-L+1))

        k++;

    LL Min=min(d[L][k],d[R-(1<<k)+1][k]);

    LL Max=max(D[L][k],D[R-(1<<k)+1][k]);

    return Max-Min;

}

void solve()

{

    LL ans=0;

    for(int i=0;i<n;i++)

    {

        int L=i,R=n-1;

        while(L<=R)  //最终L为第一个不满足的点,由于退出循环是R>L,所以R为最后一个满足的点

        {

          int Mid=(L+R)>>1;

          if(q(i,Mid) < k)

          {

              L=Mid+1;

          }

          else

            R=Mid-1;

        }

        ans+=(R-i)+1;

       // printf("%d %d %d\n",i,R,(R-i)+1);

    }

    printf("%lld\n",ans);

}

int main()

{

    int t;

    scanf("%d",&t);

    while(t--)

    {

        init();

        scanf("%d%lld",&n,&k);

        for(int i=0;i<n;i++)

        {

             scanf("%lld",&a[i]);

             d[i][0]=a[i];

             D[i][0]=a[i];

        }

        RMQ_init();

       // printf("%lld %lld %lld %lld\n",d[0][1],D[0][1],q(1,2),q(0,2));

        solve();

    }

    return 0;

}

 

 

 

你可能感兴趣的:(HDU)