码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 5289 Assignment(RMQ 单调(双端)队列)

时间:2015-07-22 19:02:34      阅读:362      评论:0      收藏:0      [点我收藏+]

标签:数学   hdu   多校2015一   

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5289


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


题意:
给出一个整数序列,求有多少个区间满足区间里的最大元素与最小元素的差不超过k”。

PS:

1:可以先用Rmq处理出区间的最值,再枚举区间!当然一味的枚举肯定没有下面两种方法快!

2:用单调(双端)队列维护区间最值

3:枚举左端点,二分右端点,用ST算法求区间最值

代码一如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
const int MAXN = 100117;

int num[MAXN];

int F_Min[MAXN][30],F_Max[MAXN][30];

void Init(int n)
{
    for(int i = 1; i <= n; i++)
    {
        F_Min[i][0] = F_Max[i][0] = num[i];
    }

    for(int i = 1; (1<<i) <= n; i++)  //按区间长度递增顺序递推
    {
        for(int j = 1; j+(1<<i)-1 <= n; j++)  //区间起点
        {
            F_Max[j][i] = max(F_Max[j][i-1],F_Max[j+(1<<(i-1))][i-1]);
            F_Min[j][i] = min(F_Min[j][i-1],F_Min[j+(1<<(i-1))][i-1]);
        }
    }
}

int Query_max(int l,int r)
{
    int k = (int)(log(double(r-l+1))/log((double)2));
    return max(F_Max[l][k], F_Max[r-(1<<k)+1][k]);
}

int Query_min(int l,int r)
{
    int k = (int)(log(double(r-l+1))/log((double)2));
    return min(F_Min[l][k], F_Min[r-(1<<k)+1][k]);
}
int solve(int l, int r)
{
    return Query_max(l,r)-Query_min(l,r);
}
int main()
{
    int t;
    int n, k;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&num[i]);
        }
        Init(n);
        __int64 ans = 0;
        int pos = 1;
        for(int i = 1; i <= n; i++)
        {
            while(solve(pos, i) >= k && pos < i)
            {
                pos++;
            }
            ans+=i-pos+1;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

代码二如下:http://www.bubuko.com/infodetail-987302.html
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std ;
#define LL __int64
deque <LL> deq1 , deq2 ;
//单调队列,deq1最大值,deq2最小值
LL a[100010] ;
int main()
{
    int t , n , i , j ;
    LL k , ans ;
    scanf("%d", &t) ;
    while( t-- )
    {
        scanf("%d %I64d", &n, &k) ;
        for(i = 0 ; i < n ; i++)
            scanf("%I64d", &a[i]) ;
        if(k == 0)
        {
            printf("0\n") ;
            continue ;
        }
        while( !deq1.empty() ) deq1.pop_back() ;
        while( !deq2.empty() ) deq2.pop_back() ;
        for(i = 0 , j = 0 , ans = 0; i < n ; i++)  //i在前,j在后
        {
            while( !deq1.empty() && deq1.back() < a[i] ) deq1.pop_back() ;
            deq1.push_back(a[i]) ;
            while( !deq2.empty() && deq2.back() > a[i] ) deq2.pop_back() ;
            deq2.push_back(a[i]) ;
            while( !deq1.empty() && !deq2.empty() && deq1.front() - deq2.front() >= k )
            {
                ans += (i-j) ;
                //printf("%d %d,%I64d %I64d\n", i , j, deq1.front() , deq2.front() ) ;
                if( deq1.front() == a[j] ) deq1.pop_front() ;
                if( deq2.front() == a[j] ) deq2.pop_front() ;
                j++ ;
            }
        }
        while( j < n )
        {
            ans += (i-j) ;
            j++ ;
        }
        printf("%I64d\n", ans) ;
    }
    return 0 ;
}

代码三如下:http://www.bubuko.com/infodetail-987919.html

#include<cstdio>
#include<cstring>
#include<cmath>
#define LL long long
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;

const int N=200007;
int minn[N][20];//2^18=262144   2^20=1048576
int maxx[N][20];

//----------------------查询O(1)-------------
int queryMin(int l,int r)
{
    int k=floor(log2((double)(r-l+1)));//2^k <= (r - l + 1),floor()向下取整函数
    return Min(minn[l][k],minn[r-(1<<k)+1][k]);
}

int queryMax(int l,int r)
{
    int k=floor(log2((double)(r-l+1)));
    return Max(maxx[l][k],maxx[r-(1<<k)+1][k]);
}
//-------------------------------------------------

int calc(int l,int r)
{
    int k=log2((double)(r-l+1));
    int MAX=Max(maxx[l][k],maxx[r-(1<<k)+1][k]);
    int MIN=Min(minn[l][k],minn[r-(1<<k)+1][k]);
    return MAX-MIN;
}

int main()
{
    int T;
    int n,k,i,j,p;
    LL ans;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        for(i=1; i<=n; ++i)
        {
            scanf("%d",&j);
            minn[i][0]=maxx[i][0]=j;
        }
//------------------------------------------预处理O(nlogn)---------------
        for(j=1; (1<<j)<=n; ++j)//1<<j==2^j,枚举区间长度1,2,4,8,16,,,,,
            for(i=1; i+(1<<j)-1<=n; ++i)//i+(1<<j)-1表示区间右边界,枚举区间左边界
            {
                p=(1<<(j-1));
                minn[i][j]=Min(minn[i][j-1],minn[i+p][j-1]);
                maxx[i][j]=Max(maxx[i][j-1],maxx[i+p][j-1]);
            }
//-----------------------------------------------------------------------

//---------------------------枚举左端点,二分右端点---------------------------

        int l,r,mid;
        ans=0;
//左端点固定为i,右端点用l,r,mid去确定,最后用l和r中的其中一个,此时l+1==r
        for(i=1; i<=n; ++i)
        {
            l=i,r=n;
            while(l+1<r)
            {
                mid=(l+r)>>1;//(l+r)/2==(l+r)>>1
                if(calc(i,mid)<k)
                {
                    l=mid;
                }
                else
                {
                    r=mid-1;//自己去演示算法流程就知道r可以赋值mid-1
                }
            }
            if(calc(i,r)<k)
            {
                ans=ans+(LL)(r-i+1);
            }
            else
            {
                ans=ans+(LL)(l-i+1);
            }
        }
//---------------------------------------------------------------------------
        printf("%lld\n",ans);
    }
    return 0;
}



版权声明:本文为博主原创文章,未经博主允许不得转载。

HDU 5289 Assignment(RMQ 单调(双端)队列)

标签:数学   hdu   多校2015一   

原文地址:http://blog.csdn.net/u012860063/article/details/47004287

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!