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 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-10-13 06:38:06

HDU 5289 Assignment(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

HDU - 5289 Assignment (RMQ+二分)

题目链接: Assignment  题意: 给出一个数列,问其中存在多少连续子序列,使得子序列的最大值-最小值<k. 题解: RMQ先处理出每个区间的最大值和最小值(复杂度为:n×logn),相当于求出了每个区间的最大值-最小值.那么现在我们枚举左端点,二分右端点就可以在n×logn×logn的时间内过. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAX_N = 1e5+9; 4 int vec[MAX_N]

单调队列(双端队列) 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 4286 Data Handler (双端队列)

Data Handler Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2455    Accepted Submission(s): 616 Problem Description You are in charge of data in a company, so you are called "Data Handler&qu

hdu5380 贪心+双端队列

http://acm.hdu.edu.cn/showproblem.php?pid=5380 Problem Description There are n+1 cities on a line. They are labeled from city 0 to city n. Mph has to start his travel from city 0, passing city 1,2,3...n-1 in order and finally arrive city n. The dista

POJ2823 Sliding Window【双端队列】

求连续的k个中最大最小值,k是滑动的,每次滑动一个 用双端队列维护可能的答案值 如果要求最小值,则维护一个单调递增的序列 对一开始的前k个,新加入的如果比队尾的小,则弹出队尾的,直到新加入的比队尾大,加入队尾 从第k+1个到最后一个,按照上述规则,压入新数,然后弹出队首元素(满足队首元素对应原来序列的位置必须在视窗内,否则,继续弹出下一个) #include <cstdio> #include <cstdlib> #include <iostream> #include

关于双端队列 deque 模板 &amp;&amp; 滑动窗口 (自出)

嗯... deque 即为双端队列,是c++语言中STL库中提供的一个东西,其功能比队列更强大,可以从队列的头与尾进行操作... 但是它的操作与队列十分相似,详见代码1: 1 #include <cstdio> 2 #include <iostream> 3 #include <deque> 4 //实际上,引用queue头文件也可以,里面包含了deque头文件 5 6 using namespace std; 7 8 deque<int> dq; //定义

8、泛型程序设计与c++标准模板库2.3双端队列容器

双端队列容器是一种放松了访问权限的队列.除了从队列的首部和尾部访问元素外,标准的双端队列也支持通过使用下标操作符"[]"进行直接访问. 它提供了直接访问和顺序访问方法.其头文件为<deque>. 1)双端队列容器的构造函数 有4中形式的构造函数: deque();//构造size()为0的双端队列容器 deque(size_type n,const T& v=T());//初始化大小为n的双端队列,第二个参数是每个元素的初始值,默认为T()构造的对象 deque(c

习题3.26双端队列

#include<stdio.h> #include<stdlib.h> struct Node; struct Queue; typedef struct Node * PtrToNode; typedef struct Queue * PtrToQ; struct Node{ PtrToNode Pre; PtrToNode Next; ElemenType Ele; } struct Queue{ PtrToNode front; PtrToNode rear; }; Ptr