2015多校第一场1002(单调队列、STL multiset、)

来源:互联网 发布:追信魔盒在线制作软件 编辑:程序博客网 时间:2024/06/05 06:55

题目链接: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): 997    Accepted Submission(s): 490


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
24 23 1 2 410 50 3 4 5 2 1 6 7 8 9
 

Sample Output
528
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)的个数。


分析:

方法1:如果直接暴力,复杂度为O(n^2)。而暴力所浪费的时间在于重复算了已经算的区间。比如第一次算得1~10区间都满足,那么2~10肯定也满足,所以以此类推可知,如果一个区间的起点包括在已算的区间内,那么对于这个点的右端点就可以从已算区间的右端点开始往右遍历。然后利用multiset这个STL容器每次添加右边的元素,同时删除左边的元素,那么就可以直接知道区间的最大值与最小值,而插入删除的时间复杂度为O(logn)。如果这样从左到右遍历的话,总共也只需遍历n个点,所以最终时间复杂度为O(nlogn)。

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int N = 1e5+10;int main(){    int T, n, k, a[N];    scanf("%d", &T);    while(T--)    {        scanf("%d%d", &n,&k);        for(int i=0; i<n; i++)            scanf("%d", a+i);        multiset<ll> ms;        multiset<ll>::iterator its, ite;        ll t = 0, res = 0;        for(ll i=0; i<n; i++)        {            its = ms.begin();            ite = ms.end();            if(!ms.empty()) ite--;            else            {                ms.insert(a[i]);                ite = ms.begin();                its = ms.begin();                t++;            }             while(t<n && (*ite)-(*its)<k)            {                ms.insert(a[t++]);                its = ms.begin();                ite = --ms.end();            }            res += t-i;            ms.erase(ms.find(a[i]));        }        printf("%lld\n", res);    }    return 0;}


方法2:

利用单调队列,思想跟上面的差不多,是尺取法的思想。

要特别注意单调队列的特性,假如插入了一个原本单调递增的序列到递增的单调队列中,那么序列不变。如果将其插入到递减的单调队列中,就会只保留值最大的元素。其实也就是队列中保留最大值或最小值,使序列单调,以最新的数为主。

(单调队列好像也被称为双端队列,队列的头和尾都可以插入以及弹出操作。一般用来求区间最值,队列中存的元素都是下标。例如求区间最大值:保证队头元素对应值最大,元素对应值从头到尾单调递减。插入元素时,将要插入元素对应的值与队尾元素对应值比,如果插入元素对应值小就舍去,大的话就弹出队尾元素,再跟现在的队尾元素对应值比,直到队列中没有比要插入元素对应值小的再将要插入元素插入到队尾。相等的话也是要更新的。最大值就是队头。如果队头元素"过期了"就把它从头部弹出。)

#include<stdio.h>#include<queue>using namespace std;typedef long long ll;const int N = 1e5+10;int main(){    int T, n, k, a[N];    scanf("%d", &T);    while(T--)    {        scanf("%d%d", &n,&k);        for(int i=0; i<n; i++)            scanf("%d", a+i);        deque<int> q1,q2; //q1递增,q2递减        ll res = 0, head = 0;        for(int i=0; i<n; i++)        {            while(!q1.empty() && a[q1.back()]>=a[i]) q1.pop_back();            q1.push_back(i); //这两行是单调队列的元素插入            while(!q2.empty() && a[q2.back()]<=a[i]) q2.pop_back();            q2.push_back(i); //插入元素的下标            while(1)            {                int index1 = q1.front();                int index2 = q2.front();                int minn = a[index1];                int maxn = a[index2];                if(maxn-minn < k) break;                if(index1 < index2) //当插入的原本序列为递增序列时                {                    head = index1+1;                    q1.pop_front();                }                else //如果原本是一个递减序列                {                    head = index2+1;                    q2.pop_front();                }            }            res += i-head+1;        }        printf("%lld\n", res);    }    return 0;}



0 0
原创粉丝点击