题目大意
给定一个n个元素的序列,m个询问,给出l,r,问有多少个点对(i,j)满足:
1. l≤i < j≤r
2. i=j-1或i,j之间的元素都严格小于min(ai,aj)
强制在线
n≤300000
分析
第2个条件很容易让人想到单调栈预处理。
按下标从小到大扫一遍序列,维护一个单调递减的栈,退栈时就可以得到一个合法的点对了。退完栈后,栈顶若还有元素,仍然能产生一个点对。
但是要注意严格小于这个条件,即退栈时,如果退出的元素等于当前元素,那么它后一个元素不能产生合法点对。
每产生一个合法点对,就用主席树存下来。就可以在线查询了。
时间复杂度
O(nlogn)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=3e5+5,M=N*39;
typedef long long LL;
int n,m,typ,ans,tot,top,st[N],Root[N],Left[M],Right[M],cnt[M],a[N];
char c;
int read()
{
for (c=getchar();c<'0' || c>'9';c=getchar());
int x=c-48;
for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x;
}
void insert(int l,int r,int v,int &x,int y)
{
x=++tot;
Left[x]=Left[y]; Right[x]=Right[y]; cnt[x]=cnt[y]+1;
if (l==r) return;
int Mid=l+r>>1;
if (v<=Mid) insert(l,Mid,v,Left[x],Left[y]);else insert(Mid+1,r,v,Right[x],Right[y]);
}
int query(int l,int r,int v,int x,int y)
{
if (l==v) return cnt[x]-cnt[y];
int Mid=l+r>>1;
if (v>Mid) return query(Mid+1,r,v,Right[x],Right[y]);
return query(l,Mid,v,Left[x],Left[y])+cnt[Right[x]]-cnt[Right[y]];
}
int main()
{
n=read(); m=read(); typ=read();
for (int i=1;i<=n;i++)
{
a[i]=read();
Root[i]=Root[i-1];
for (;top>0 && a[st[top]]<a[i];top--) insert(1,n,st[top],Root[i],Root[i]);
if (top>0) insert(1,n,st[top],Root[i],Root[i]);
for (;top>0 && a[st[top]]==a[i];top--);
st[++top]=i;
}
while (m--)
{
int l=read(),r=read();
if (typ)
{
l=(l+ans-1)%n+1; r=(r+ans-1)%n+1;
if (l>r) l^=r^=l^=r;
}
printf("%d\n",ans=query(1,n,l,Root[r],Root[l-1]));
}
return 0;
}