[BZOJ3956]Count(单调栈+线段树)

题目描述

传送门

题解

感觉这道题蕴含了一些十分巧妙的性质。
可以发现好点对实际上很少,最多不超过2n个。当一个点作为点对中的较小点的时候,最多只能和它左边第一个大于等于它的点以及它右边第一个大于等于它的点配对。
维护一个单调递减的栈就可以求出所有的好点对。
还可以发现对于一段询问的区间,找出区间中最大的点的位置,那么区间中的所有好点对都不会跨越这个点。那么就计算左端点在最大点左边的个数,右端点在最大点右边的个数就可以了。时间复杂度 O(nlogn) ,用 O(1) rmq实际上是可以做到 O(n) 的。

代码

#include
#include
#include
using namespace std;

const int N=1e6+5;
const int T=N*4;

int n,m,type,top,cnt,l,r,loc,ans;
int a[N],ls[N],rs[N],stack[N],maxn[N*4];
struct hp{int l,r;}cp[N*2];

inline int read()
{
    char ch=getchar(); int x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline void out(int x)
{
    if (x==0) {puts("0");return;}
    int now=0; int print[10];
    while (x>0)
    {
        print[++now]=x%10;
        x/=10;
    }
    for (int i=now;i>=1;--i) putchar(print[i]+'0');
    putchar('\n');
}
inline void pre()
{
    for (int i=1;i<=n;++i)
    {
        int last=0;
        while (a[i]>a[stack[top]]&&top)
        {
            if (top>1) cp[++cnt].l=stack[top-1],cp[cnt].r=stack[top];
            if (a[stack[top]]!=last) cp[++cnt].l=stack[top],cp[cnt].r=i;
            last=a[stack[top]]; top--;
        }
        stack[++top]=i;
    }
    while (top>1) cp[++cnt].l=stack[top-1],cp[cnt].r=stack[top],top--;
    for (int i=1;i<=cnt;++i) ls[cp[i].l]++;
    for (int i=1;i<=cnt;++i) rs[cp[i].r]++;
    for (int i=2;i<=n;++i) ls[i]+=ls[i-1];
    for (int i=2;i<=n;++i) rs[i]+=rs[i-1];
}
inline void update(int now)
{
    if (a[maxn[now<<1]]>=a[maxn[now<<1|1]]) maxn[now]=maxn[now<<1];
    else maxn[now]=maxn[now<<1|1];
}
inline void build(int now,int l,int r)
{
    int mid=(l+r)>>1;
    if (l==r)
    {
        maxn[now]=l;
        return;
    }
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
inline int query(int now,int l,int r,int lrange,int rrange)
{
    int mid=(l+r)>>1,ans=0;
    if (lrange<=l&&r<=rrange) return maxn[now];
    if (lrange<=mid)
    {
        int ansl=query(now<<1,l,mid,lrange,rrange);
        if (a[ansl]>=a[ans]) ans=ansl;
    }
    if (mid+1<=rrange)
    {
        int ansr=query(now<<1|1,mid+1,r,lrange,rrange);
        if (a[ansr]>a[ans]) ans=ansr;
    }
    return ans;
}
int main()
{
    n=read(); m=read(); type=read();
    for (int i=1;i<=n;++i) a[i]=read();
    pre();
    build(1,1,n);
    for (int i=1;i<=m;++i)
    {
        int ll=read(); int rr=read();
        if (type==1)
        {
            l=min((ll+ans-1)%n,(rr+ans-1)%n)+1;
            r=max((ll+ans-1)%n,(rr+ans-1)%n)+1;
        }
        else l=ll,r=rr;
        loc=query(1,1,n,l,r);
        ans=ls[loc-1]-ls[l-1]+rs[r]-rs[loc];
        out(ans);
    }
}


总结

①多想想题目中隐藏的条件和性质,可以用奇怪的方法证明。

你可能感兴趣的:(题解,线段树,单调栈)