zoukankan      html  css  js  c++  java
  • [CERC2017]Intrinsic Interval——扫描线+转化思想+线段树

    [CERC2017]Intrinsic Interval

    https://www.luogu.org/blog/ywycasm/solution-p4747#

    这种“好的区间”,见得还是比较多的了。

    mx-mi=r-l

    比较经典的题是统计这样的区间个数。可以分治+大力分类讨论mx,mi的位置

    但是这个题是一个询问。

    先观察一些显而易见的性质:

    1.好的区间的交也是好的区间

    2.好的区间的并也是好的区间

    所以,考虑对于一个询问,如果往后固定r找到了一个[L,R]的L左边最靠后的l,使得[l,r]是好区间,那么这就是最优答案了。后面再有也一定是包含关系。

    固定r的话,不如试试离线扫描线?

    用set存询问,L大到小排序。不合法直接break,因为更小的区间更不可能有左端点。合法更新答案,然后erase

    问题在于,当r变成r+1的时候,我们怎样快速维护每个li的位置是不是有[li,r+1]是一个好的区间

    一个比较棒的转化是:

    如果[l,r]是一个好的区间,那么把这个区间的数排序之后,得到的序列相邻两项相差一定是1

    定义(i,j)为一个好的二元组,当且仅当a[i]-a[j]=1

    这样的两项的二元组在[l,r]中恰好有r-l个

    所以,一个区间是好的区间,当且仅当好的二元组有r-l个。

    线段树每个位置维护到r的二元组个数val

    val+l=r则l位置是好的区间

    l不变,不妨线段树位置初值为下标。

    更新的话,设p[i]为权值为i的数的数组下标

    多了一个a[r],对于左端点位于[1,p[a[r]-1]]和[1,p[a[r]+1]]的到r都会多一个二元组!

    所以这些位置区间+1

    存在val+l=r的找最右边的l?

    发现tmp=val+l,最大就是r!(因为val最多就是r-l)

    所以区间最大值最右边的那一个位置。

    线段树随便搞

    #include<bits/stdc++.h>
    #define reg register int
    #define mid ((l+r)>>1)
    #define ls (x<<1)
    #define rs (x<<1|1)
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=100000+5;
    int n,m;
    int a[N],p[N];
    struct po{
        int mx,id;
        po(){}
        po(int vv,int dd){
            mx=vv,id=dd;
        }
        bool friend operator <(po a,po b){
            if(a.mx!=b.mx) return a.mx<b.mx;
            return a.id<b.id;
        }
    }pr;
    struct node{
        po v;
        int ad;
    }t[4*N];
    void pushup(int x){
        t[x].v=max(t[ls].v,t[rs].v);
    }
    void pushdown(int x){
        if(!t[x].ad) return;
        t[ls].v.mx+=t[x].ad;
        t[ls].ad+=t[x].ad;
        t[rs].v.mx+=t[x].ad;
        t[rs].ad+=t[x].ad;
        t[x].ad=0;
    }
    void build(int x,int l,int r){
        if(l==r){
            t[x].v=po(l,l);
            t[x].ad=0;
            return;
        }
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(x);
    }
    void add(int x,int l,int r,int L,int R,int c){
        if(L<=l&&r<=R){
            t[x].ad+=c;
            t[x].v.mx+=c;
            return;
        }
        pushdown(x);
        if(L<=mid) add(ls,l,mid,L,R,c);
        if(mid<R) add(rs,mid+1,r,L,R,c);
        pushup(x);
    }
    po query(int x,int l,int r,int L,int R){//ask the rightest mx's pos
        if(L<=l&&r<=R) return t[x].v;
        pushdown(x);
        po ret;ret.mx=-2333,ret.id=-6666;
        if(L<=mid) ret=max(ret,query(x<<1,l,mid,L,R));
        if(mid<R) ret=max(ret,query(x<<1|1,mid+1,r,L,R));
        return ret;
    }
    struct que{
        int l,r,id;
        bool friend operator <(que a,que b){
            if(a.l!=b.l) return a.l>b.l;
            if(a.r!=b.r) return a.r>b.r;
            return a.id<b.id;
        }
    }q[N];
    bool cmp(que a,que b){
        return a.r<b.r;
    }
    
    pair<int,int>ans[N];
    set<que>s;
    set<que>::iterator it;
    int main(){
        rd(n);
        for(reg i=1;i<=n;++i) rd(a[i]),p[a[i]]=i;
        rd(m);
        for(reg i=1;i<=m;++i){
            rd(q[i].l);rd(q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1,cmp);
        build(1,1,n);
        int ptr=1;
        for(reg i=1;i<=n;++i){
            while(ptr<=m&&q[ptr].r==i){
                s.insert(q[ptr]);++ptr;
            }
            if(a[i]>1&&p[a[i]-1]<i) add(1,1,n,1,p[a[i]-1],1);
            if(a[i]<n&&p[a[i]+1]<i) add(1,1,n,1,p[a[i]+1],1);
            while(s.size()){
                que now=*s.begin();
                pr=query(1,1,n,1,now.l);
                if(pr.mx!=i){
                    break;
                }
                ans[now.id].first=pr.id;
                ans[now.id].second=i;
                
                s.erase(now);
            }
        }
        for(reg i=1;i<=m;++i){
            printf("%d %d
    ",ans[i].first,ans[i].second);
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/25 19:27:07
    */

    (mx-mi=r-l l+mx-mi=r,这个是不可以维护的,因为第一不好维护,第二没有最大值就是r的限制,不能查找)

    总结:

    离线+扫描线是处理区间询问的利器

    尤其这个题,r对l的贡献又相对独立,而且可以支持快速单点增量改变,所以扫描线处理起来就游刃有余。

    好区间的交、二元组的性质也是重要的思维过程。

     upda:2019.3.17:主要还是利用二元组的转化思想,使得贡献独立

  • 相关阅读:
    如何解决C#异常:必须先将当前线程设置为单线程单元(STA)模式,然后才能进行OLE调用,请确保你的Main函数已在其上标记了STAThreadAttribute
    go多态
    go泛型
    protoc工具使用
    grpc protobuf协议
    grpc根据proto文件自动生成go源码
    go安装grpc
    go protobuf
    go读取http.Request中body的内容
    go数据库操作
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10176315.html
Copyright © 2011-2022 走看看