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

题目链接: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

2015 Multi-University Training Contest 1

题意:

给出一个整数序列,求有多少个区间满足区间里的最大元素与最小元素的差不超过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;
}

代码二例如以下:

#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 ;
}

代码三例如以下:

#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;
}
时间: 2024-09-28 06:59:53

HDU 5289 Assignment(多校2015 RMQ 单调(双端)队列)的相关文章

Vijos1834 NOI2005 瑰丽华尔兹 动态规划 单调双端队列优化

设dp[t][x][y]表示处理完前t个时间段,钢琴停留在(x,y)处,最多可以走多少个格子 转移时只需逆着当前倾斜的方向统计len个格子(len为时间区间的长度,len=t-s+1),如果遇到障碍就中断 转移过程可以用单调非递增的双端队列优化 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 const int maxN=202; 6 const int inf=0x3f3f3f3f

单调队列(双端队列) poj2823 hdoj3415 hdoj3530

单调队列及其应用(双端队列) 单调队列,望文生义,就是指队列中的元素是单调的.如:{a1,a2,a3,a4--an}满足a1<=a2<=a3--<=an,a序列便是单调递增序列.同理递减队列也是存在的. 单调队列的出现可以简化问题,队首元素便是最大(小)值,这样,选取最大(小)值的复杂度便为o(1),由于队列的性质,每个元素入队一次,出队一次,维护队列的复杂度均摊下来便是o(1). 如何维护单调队列呢,以单调递增序列为例: 1.如果队列的长度一定,先判断队首元素是否在规定范围内,如果超范

从0开始学算法--数据结构(2.4双端队列与单调队列)

双端队列是特殊的队列,它与队列不同的是可以将元素加入头或尾,可以从头或尾取出元素(滑稽-这部就是栈和队列结合了吗). c++标准库 头文件 #include<deque> 定义 deque<int>deq; 取出队头,尾元素 deq.pop_front(); deq.pop_back(); 访问队头,尾元素 deq.front(); deq.back(); 向队头,尾加入元素 deq.push_front(x); deq.push_back(x); 单调队列是在队列的基础上使它保持

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

题目链接: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 tas

hdu 5289 Assignment 二分+rmq

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5289 Assignment Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 332    Accepted Submission(s): 169 Problem Description Tom owns a company and he is

HDU 5289 Assignment(二分+RMQ-ST)

Assignment Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 3813    Accepted Submission(s): 1771 Problem Description Tom owns a company and he is the boss. There are n staffs which are numbered

hdu 5289 Assignment(2015多校第一场第2题)RMQ+二分(或者multiset模拟过程)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5289 题意:给你n个数和k,求有多少的区间使得区间内部任意两个数的差值小于k,输出符合要求的区间个数 思路:求出区间的最大最小值,只要他们的差值小于k,那么这个区间就符合要求,但是由于n较大,用暴力一定超时,所以就要用别的方法了:而RMQ是可以求区间的最值的,而且预处理的复杂度只有O(nlogn),而查询只是O(1)处理,这样相对来说节约了时间,再根据右端点来二分枚举左端点(其实不用二分好像更快,估

HDU 5289 Assignment(2015 多校第一场二分 + RMQ)

Assignment Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 627    Accepted Submission(s): 318 Problem Description Tom owns a company and he is the boss. There are n staffs which are numbered fr

[多校2015.01.1002 单调队列] hdu 5289 Assignment

题意: 给你n个人和一个k 问你把这n个人分成多少个连续的子区间,要求区间每个数两两相差绝对值小于k 思路: 我们仅仅只需要对于当前位置,最左边那个和它绝对值相差大于等于k 的位置在哪 假设对于i这个位置,最左边的位置是tep,不存在的话tep=0 那么当且位置的贡献就是 sum[i]=min(i-tep,sum[i-1]+1); 那么对于这个位置怎么求的话,我是使用了两个单调队列 第一次维护递增的,第二次维护递减的. 每次先把不满足的点全部出队,然后记录最大的下标 然后入队 代码: #incl