题目
https://ac.nowcoder.com/acm/contest/82672/G
思路来源
出题人 涼風青葉7代码
题解
注意到三种情况即可,
第一种情况,10 9 8 1 2,保留1
第二种情况,6 5 10 9 4 4,保留5 4 4
第三种情况,6 5 4,全保留
所以,策略是:
(1) 考察区间最小值,如果最小值后如果有增的,那么最小值前也都得删完
(2) 区间最小值后面比最小值大的都是得删的,只有小于等于最小值的才能留着
(3) 如果最小值出现在末尾,[l,r]从左往右一直递减,那么操作次数为0
(4) 如果(1)-(3)都不满足,那么一定是最小值出现在末尾,
并且存在p(l<p<r)使得区间[p,r]满足(3),
此时[l,p-1]还有最小值w,对[l,p-1]的最小值w用(1),对[p,r]用(2)
也就是,[l,p-1]区间内只保留最小值w,[p,r]区间内只保留不超过w的值,此时只有两种数字
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=18;
int n,q,a[N],b[N][M],dp[N],lg[N],l,r;
map<int,vector<int>>mp;
int rmq(int l,int r){
int k=lg[r-l+1];
return min(b[l][k],b[r-(1<<k)+1][k]);
}
int cnt(int v,int l,int r){
auto &x=mp[v];
int c1=upper_bound(x.begin(),x.end(),r)-x.begin();
int c2=upper_bound(x.begin(),x.end(),l-1)-x.begin();
return c1-c2;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
if(i>=2)lg[i]=lg[i>>1]+1;
mp[a[i]].push_back(i);
b[i][0]=a[i];
if(i==1 || a[i]<=a[i-1])dp[i]=dp[i-1]+1;
else dp[i]=1;
}
for(int j=1;j<=17;++j){
for(int i=1;i+(1<<j)-1<=n;++i){
b[i][j]=min(b[i][j-1],b[i+(1<<(j-1))][j-1]);
}
}
while(q--){
scanf("%d%d",&l,&r);
int sz=r-l+1;
if(dp[r]>=sz){
puts("0");
}
else{
int st=r-dp[r]+1;
int v2=rmq(l,st-1),v=rmq(l,r);
if(v2==v){
printf("%d\n",sz-cnt(v,l,r));
}
else{
int np=lower_bound(a+st,a+r+1,v2,greater<int>())-a;//<=v2
printf("%d\n",sz-cnt(v2,l,st-1)-(r-np+1));
}
}
}
return 0;
}