【bzoj3956】【Count】【主席树+单调栈】

Description

Input

Output

Sample Input

3 2 0
2 1 2
1 1
1 3

Sample Output

0
3

HINT

M,N<=3*10^5,Ai<=10^9

题解:可以发现点对数不会大于2*n,且点对之间不跨立.
           所以我们可以先用单调栈求出所有的点对,主席树维护一下即可.
           注意处理数值相等的情况.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300010
using namespace std;
int a[N],ls[N*42],rs[N*42],sum[N*42],n,m,tp,cnt,num,top,l,r,ans,root[N];
struct use{int val,id;}q[N];
struct point{int l,r;}p[N*2];
bool cmp(point a,point b){return a.l==b.l?a.r<b.r:a.l<b.l;}
int read(){
  int x=0;char ch=getchar();
  while (ch<'0'||ch>'9') ch=getchar();
  while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x;
}
void insert(int x,int &y,int l,int r,int p){
   y=++cnt;
   ls[y]=ls[x];rs[y]=rs[x];
   if (l==r) {sum[y]=sum[x]+1;return;}
   int mid=(l+r)>>1;
   if (p<=mid) insert(ls[x],ls[y],l,mid,p);
   else insert(rs[x],rs[y],mid+1,r,p);
   sum[y]=sum[ls[y]]+sum[rs[y]];
} 
int query(int x,int y,int l,int r,int ll,int rr){
   int mid=(l+r)>>1;
   if (ll==l&&r==rr) return sum[y]-sum[x];
   if (rr<=mid) return query(ls[x],ls[y],l,mid,ll,rr);
   else if (mid<ll) return query(rs[x],rs[y],mid+1,r,ll,rr);
   else return query(ls[x],ls[y],l,mid,ll,mid)+query(rs[x],rs[y],mid+1,r,mid+1,rr); 
}
int main(){
  n=read();m=read();tp=read();
  for (int i=1;i<=n;i++) a[i]=read(); 
  top=0;num=0;
  for (int i=1;i<=n;i++){
    int flag(0);
    while (top&&a[i]>=q[top].val){p[++num]=point{q[top].id,i};if (a[i]==q[top].val) flag=1;top--;}
    if (top&&!flag) p[++num]=point{q[top].id,i};
    q[++top]=use{a[i],i};
  }     
  sort(p+1,p+num+1,cmp);top=1;
  for (int i=1;i<=n;i++){
    root[i]=root[i-1];
    while (top<=num&&p[top].l==i){insert(root[i],root[i],1,n,p[top].r);top++;} 
  }
  for (int i=1;i<=m;i++){
     l=read();r=read();
     if(tp){l=(l+ans-1)%n+1;r=(r+ans-1)%n+1;if (l>r)swap(l,r);}
     ans=query(root[l-1],root[r],1,n,l,r);
     printf("%d\n",ans);
  }
} 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值