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];
 5 int dp1[MAX_N][25];
 6 int dp2[MAX_N][25];
 7 long long ans = 0;
 8 int N,M,T;
 9 void ST(int N)
10 {
11     for(int i=1;i<=N;i++) dp1[i][0] = dp2[i][0] = vec[i];
12     for(int j=1;(1<<j)<=N;j++)
13     {
14         for(int i=1;i+(1<<j)-1<=N;i++)
15         {
16             dp1[i][j] = max(dp1[i][j-1],dp1[i+(1<<j-1)][j-1]);
17             dp2[i][j] = min(dp2[i][j-1],dp2[i+(1<<j-1)][j-1]);
18         }
19     }
20 }
21 int RMQ(int l,int r)
22 {
23     int k = 0;
24     while(1<<k+1 <= r-l+1) k++;
25     int maxn = max(dp1[l][k],dp1[r-(1<<k)+1][k]);
26     int minn = min(dp2[l][k],dp2[r-(1<<k)+1][k]);
27     return maxn-minn;
28 }
29 int main()
30 {
31     cin>>T;
32     while(T--)
33     {
34         cin>>N>>M;
35         ans = 0;
36         for(int i=1;i<=N;i++) scanf("%d",&vec[i]);
37         ST(N);
38         for(int i=1;i<=N;i++)
39         {
40             int l =i,r = N;
41             while(l<=r)
42             {
43                 int mid = (l+r)>>1;
44                 if(RMQ(i,mid) < M) l = mid+1;
45                 else r = mid-1;
46             }
47             ans += (l-1) - i +1;
48         }
49         cout<<ans<<endl;
50     }
51     return 0;
52 }
View Code

  还有一种解法是用两个单调队列维护区间的最大和最小值,让我收益颇多@。@!用双端队列构成单调队列,一个维护最大值,一个维护最小值,从左向右枚举右端点。现在我们知道了这个区间的最大值和最小值,如果最大值减去最小值是小于k的则当前的左端点到右端点里面所有的区间都是符合条件的。直到枚举到一个右端点,使得最大值减最小值>=k,则开始移动左端点删去两个队列中左端点的值(如果有的话),直到区间重新符合条件。用单调队列处理的话复杂度为(O(n))。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAX_N = 1e5+9;
 4 long long vec[MAX_N];
 5 deque<long long> que1,que2;
 6 int main()
 7 {
 8     long long N,M,T;
 9     cin>>T;
10     while(cin>>N>>M)
11     {
12         while(!que1.empty()) que1.pop_back();
13         while(!que2.empty()) que2.pop_back();
14         for(int i=0;i<N;i++) scanf("%lld",&vec[i]);
15         int pos = 0;
16         long long ans = 0;
17         for(int i=0;i<N;i++)
18         {
19             while(!que1.empty() && que1.back() < vec[i]) que1.pop_back();
20             que1.push_back(vec[i]);
21             while(!que2.empty() && que2.back() > vec[i]) que2.pop_back();
22             que2.push_back(vec[i]);
23             while(!que1.empty() && !que2.empty() && que1.front() - que2.front() >= M)
24             {
25                 ans += (i-pos);
26                 if(que1.front() == vec[pos]) que1.pop_front();
27                 if(que2.front() == vec[pos]) que2.pop_front();
28                 pos++;
29             }
30         }
31         while(pos < N)
32         {
33             ans += (N-pos);
34             pos ++;
35         }
36         cout<<ans<<endl;
37     }
38     return 0;
39 }
View Code

 

posted @ 2018-02-01 21:44  会打架的程序员不是好客服  阅读(239)  评论(0编辑  收藏  举报