bzoj3956: Count(主席树+单调栈)
bzoj3956: Count(主席树+单调栈)
思路
对友好点对建边的话,可以看出最多只有2n条边,先用单调栈使所有左端点记录右端点,然后对左端点前缀建权值主席树,查询的时候只要判断T[r] - T[l-1] 这颗主席树中有多少点在[l,r]这段区间就行了。
代码
#include <bits/stdc++.h>
using namespace std;
#define dd(x) cout<<#x<<"="<<x<<" "
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define de(x) cout<<#x<<"="<<x<<"\n"
#define mes(p) memset(p,0,sizeof(p))
#define fi first
#define se second
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define sz(x) (int)x.size()
#define pb push_back
#define ls (rt<<1)
#define rs (ls|1)
#define all(x) x.begin(),x.end()
const int maxn=300005;
typedef long long ll;
typedef vector <int > vi;
typedef pair <int, int> pi;
int n, m, ty, a[maxn], T[maxn], cnt;
struct node{
int sum, l,r;
node() {sum=l=r=0;}
}t[maxn<<6];
vector <int > v[maxn];
void update(int pre,int &now,int l,int r,int pos){
now = ++cnt ;
t[now].sum= t[pre].sum+1;
if(l==r) return ;
t[now].l= t[pre].l;
t[now].r= t[pre].r;
int mid =l+ r >> 1;
if(pos <= mid ) update(t[pre].l,t[now].l,l,mid,pos);
else update(t[pre].r,t[now].r,mid+1,r,pos);
}
int qr(int x,int y,int l,int r,int xx,int yy){
if(l>=xx && r<=yy){
return t[y].sum-t[x].sum;
}
int mid = l+r>>1, s=0;
if(xx<=mid) s+=qr(t[x].l,t[y].l,l,mid,xx,yy);
if(mid<yy) s+= qr(t[x].r,t[y].r,mid+1,r,xx,yy);
return s;
}
int main(){
scanf("%d%d%d",&n,&m,&ty);
rep(i,1,n+1) scanf("%d",&a[i]);
stack< int > s;
rep(i,1,n+1){
while(sz(s) &&a[s.top()] <= a[i]) {
v[s.top()].pb(i);
s.pop();
}
s.push(i);
}
while(sz(s)) s.pop();
for(int i=n;i>0;i--){
while(sz(s)&& a[s.top()] <= a[i]){
v[i].pb(s.top());
s.pop();
}
s.push(i);
}
rep(i,1,n+1){
T[i] = T[i-1];
sort(all(v[i]));
rep(j,0,sz(v[i]))
if(!j || v[i][j]!=v[i][j-1])
update(T[i],T[i],1,n,v[i][j]);
}
int last =0 ;
while(m--){
int x, y;
scanf("%d%d",&x,&y);
if(ty) x=(x+last-1)%n + 1 , y =(y+last-1)%n+1;
int l = min(x,y);
y=x+y-l;
x=l;
last = qr(T[x-1],T[y],1,n,x,y);
printf("%d\n",last);
}
return 0;
}