bzoj3956 Count 解题报告

题目大意:
给出 n 个数,定义一种好点对(i,j)( i<j) 需要满足如下两个条件之一:
1. i=j1
2.对于 k(i,j) ,均有 a[k]<min(a[i],a[j])
询问若干个区间中的好点对个数。

题解:
我们首先可以发现,这样的好点对是不相交的(但会出现包含情况),既不可能出现两个好点对 (i1,j1),(i2,j2) ,使得 j1>i2 j1<j2 ,因为那样的话这两个好点对就会变成 (i1,i2),(j1,j2)
那这样的话我们可以预处理出来以每个点作为 i j(i<j)的好点对数量,显然只用从这个点向前或向后扫到第一个大于等于它的点,那这就可以正反扫两遍,用单调栈维护一下就 OK ,然后对这两种情况分别做前缀和 sl,sr
然后我们需要知道一个区间中的最大值,这个用 ST O(nlog(n)) 的预处理一下就可以 O(1) 的查询。
对于每个询问区间 [l,r] ,我们先查出区间的最大值,有两种情况:
1.如果这个最大值是 l r中的一个,那区间的好点对个数就是 slrsll1
2.如果是在区间中间,那就以这个最大值为界,将原区间分成 [l,maxx],[maxx,r] ,那这两个区间就都变成了情况1。
Ps. 答案不会超过 2n

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 300010
#define LL long long
int n,m,a[N],q[N<<1],l[N],r[N],st[N][20],w[N],ans,sl[N],sr[N];
inline int in(){
    int x=0; char ch=getchar(); bool f=1;
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=0;
        ch=getchar();
    }while (ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=getchar();
    if (!f) x=-x; return x;
}
inline void Init(){
    int i,j,top=1,x,y; q[top]=1;
    for (i=2; i<=n; i++){
        while (top&&a[i]>a[q[top]]) r[i]++,top--;
        if (top) r[i]++;
        while (top&&a[i]>=a[q[top]]) top--;
        q[++top]=i;
    }for (i=1; i<=n; i++) sr[i]=sr[i-1]+r[i];
    top=1,q[top]=n;
    for (i=n-1; i>=1; i--){
        while (top&&a[i]>a[q[top]]) l[i]++,top--;
        if (top) l[i]++;
        while (top&&a[i]>=a[q[top]]) top--;
        q[++top]=i;
    }for (i=1; i<=n; i++) sl[i]=sl[i-1]+l[i];
    for (i=1; i<=n; i++) st[i][0]=i;
    for (j=1; (1<<j)<=n; j++)
        for (i=1; i+(1<<(j-1))<=n; i++){
            x=st[i][j-1],y=st[i+(1<<(j-1))][j-1];
            if (a[x]>a[y]) st[i][j]=x;
            else st[i][j]=y;
        }
    for (i=1,j=0; i<=n; i++){
        if ((1<<(j+1))<=i) j++;
        w[i]=j;
    }
}
inline int query(int l,int r){
    int k=w[r-l],x,y;
    x=st[l][k],y=st[r-(1<<k)+1][k];
    if (a[x]>a[y]) return x;
    else return y;
}
inline void work(int l,int r){
    int x=query(l,r);
    if (a[x]>=min(a[l],a[r]))
        ans=sl[x-1]-sl[l-1]+sr[r]-sr[x];
    else ans=sl[r]-sl[l-1];
    printf("%d\n",ans);
}
int main(){
    int i,l,r,opt;
    n=in(),m=in(),opt=in();
    for (i=1; i<=n; i++) a[i]=in();
    Init(),ans=0;
    for (i=1; i<=m; i++){
        l=in(),r=in();
        if (opt) l=(l+ans-1)%n+1,r=(r+ans-1)%n+1;
        if (l>r) swap(l,r); work(l,r);
    }return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值