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 }
还有一种解法是用两个单调队列维护区间的最大和最小值,让我收益颇多@。@!用双端队列构成单调队列,一个维护最大值,一个维护最小值,从左向右枚举右端点。现在我们知道了这个区间的最大值和最小值,如果最大值减去最小值是小于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 }