• P4747 [CERC2017]Intrinsic Interval


      题面

      接下来就要开始了,听说这是个黑题,我尽量好好写。请准备一下纸和笔(不用)。。。。。

      首先引理1:两个优美区间的交集是优美区间,不含包含的情况,这个应该手模一下就能证明。。。

        引理2:定义一个区间的num就是区间内数值相差1的点对数,那么得到优美区间的等价条件就是l+num=r这个可以理解一下,例如1 7 6的num就是1(对),则1+1<3因此不是优美区间,而5 7 6的num是2则1+2=3所以是优美。证明的话我们可以考虑把序列排序,num就代表从最小值开始加,每次只能加1,能加多少次的,那么在例1中只能连续加一次,例2可以连续加2次就是加到头,这就是优美区间。

        引理3:k+num(k,i)<=i,这个是显然的,点对不能超过区间长度

      然后就是我们的结论:从r枚举优美区间的右端点,第一个能够包住[l,r]的优美区间就是答案

      

      于是上图就出现了,假如我枚举r1算到了l1,然后l2和r2其实是最优解怎么办,但是这种东西是不存在的,因为你由引理1可以知道[l2,r1]也是一个优美区间,那么枚举r1是直接得到l2的,而非l1。

      当然我们不是对没一个区间做一个这样的枚举,那样铁定炸飞,我们应该像CDQ一样,对一堆区间做一遍枚举,信息尽量重复利用。那么我们的思路逐渐清晰,就是要枚举每一个i然后处理所有右端点在i之前的没被回答的询问。

      但是好像又遇到了问题,假设在枚举一个i的时候,我要得到的信息是怎样的,当我要处理询问[l,r]时,我要得到一个最靠右的k使得,k(属于)[1,l],并且k+num(k,i)=i(要找到一个优美区间[k,i]并且能抱住[l,r])。然后就死了,这**怎么用线段树维护啊。。。

      挤细西考一下,有引理3,那么就可以搞了,维护一个k+num(k,i)的最大值及其位置,每次直接查(1,l)中的最大值val,要么=i,要么<i,如果val=i显然合法。那么每次枚举到一个新的点i,就要更新一下区间里的所有点的num,把num(k,i)变成num(k,i+1),其实这个很好维护,就是对于i,和它值差1的就2个,分别是a[i]+1和a[i]-1,它们的位置是pos[a[i]-1],pos[a[i]+1],如果一个pos出现在i之前那么这个i和pos就能组成一对点,所有的1<k<pos都有num(k,i)要+1,因为在1-pos之间的k到i的点对显然多了一对i和pos。

      整理下思路,每次把枚举一对点,把我现在能处理的区间,就是右端点等于i的放到一个待回复集合中,让它左端点有序,原因是如果出现不合法能够快速跳走。我把询问的所有l从大到小排序,直接得到1,l中的k+num(k,i)最大值及最靠右的k,如果小于i那么1,l中任意一个数和i组成的区间都无法满足,剩下一堆区间直接不用算,因为更小的k绝对不可能查出来更大的k+num(k,i),此时一定无法满足,只能等到下一个i才可能满足。然后就结束了。用个堆或者set都行我不会用set。然而某wmz以及二营长还有mikufun都没素质卡分块调块长直接碾标算。。听说还有猿小鲲。。

       

    #include<iostream>
    #include<queue>
    #include<cstdio>
    #include<vector>
    using namespace std;
    const int N=100020;
    int a[N],pos[N],ans[N][2],tt,n,m;
    struct tree{int l,r,w,p,f;}tr[N*4];
    struct node
    {
        int l,r,id;
        bool friend operator <(node a,node b)
        {return a.l<b.l;}
    };
    struct pub{int w,p;};
    vector<node> v[N];
    priority_queue<node> q;
    inline int rd()
    {
        int s=0,w=1;
        char cc=getchar();
        while(cc<'0'||cc>'9') {if(cc=='-')w=-1;cc=getchar();}
        while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar();
        return s*w;
    }
    inline void change(const register int k,const register int w)
    {
        tr[k].w+=w;
        tr[k].f+=w;
    }
    inline pub merge(const register pub a,const register pub b)
    {
        pub c;
        c.w=max(a.w,b.w);
        if(a.w>b.w) c.p=a.p;
        else c.p=b.p;
        return c;
    }
    inline void updata(const register int k)
    {
        tr[k].w=max(tr[k<<1].w,tr[k<<1|1].w);
        if(tr[k<<1].w>tr[k<<1|1].w) tr[k].p=tr[k<<1].p;
        else tr[k].p=tr[k<<1|1].p;
    }
    inline void down(const register int k)
    {
        change(k<<1,tr[k].f);change(k<<1|1,tr[k].f);
        tr[k].f=0;
    }
    void build(const register int k,const register int l,const register int r)
    {
        tr[k].l=l;tr[k].r=r;tr[k].f=0;
        if(l==r){tr[k].p=tr[k].w=l;return;}
        const register int mid=l+r>>1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        updata(k);
    }
    void add(const register int k,const register int x,const register int y)
    {
        int l=tr[k].l,r=tr[k].r,mid=l+r>>1;
        if(l==x&&r==y){change(k,1);return;}
        if(tr[k].f) down(k);
        if(y<=mid) add(k<<1,x,y);
        else if(x>mid) add(k<<1|1,x,y);
        else add(k<<1,x,mid),add(k<<1|1,mid+1,y);
        updata(k);
    }
    pub ask(const register int k,const register int x,const register int y)
    {
        int l=tr[k].l,r=tr[k].r,mid=l+r>>1;
        if(l==x&&r==y) return (pub){tr[k].w,tr[k].p};
        if(tr[k].f) down(k);
        if(y<=mid) return ask(k<<1,x,y);
        else if(x>mid) return ask(k<<1|1,x,y);
        return merge(ask(k<<1,x,mid),ask(k<<1|1,mid+1,y));
    }
    int main()
    {
        n=rd();
        build(1,1,n);
        for(int i=1;i<=n;i++)
            a[i]=rd(),pos[a[i]]=i;
        m=rd();
        for(int i=1;i<=m;i++)
        {
            int l=rd(),r=rd();
            v[r].push_back((node){l,r,i});
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i]-1>=1&&pos[a[i]-1]<i)add(1,1,pos[a[i]-1]);
            if(a[i]+1<=n&&pos[a[i]+1]<i)add(1,1,pos[a[i]+1]);
            for(int j=0;j<v[i].size();j++) q.push(v[i][j]);
            while(!q.empty())
            {
                node tmp=q.top();
                pub temp=ask(1,1,tmp.l);
                if(temp.w!=i) break;
                else ans[tmp.id][0]=temp.p,ans[tmp.id][1]=i,q.pop();
            }
        }
        for(int i=1;i<=m;i++)printf("%d %d
    ",ans[i][0],ans[i][1]);
    }
    /*
    g++ 1.cpp -o 1
    ./1
    7
    3 1 7 5 6 4 2
    3
    3 6
    7 7
    1 3
    */
    View Code

      

  • 相关阅读:
    基于bootstrap实现收缩导航条
    js判断打开网站页面是PC端还是手机端
    vs2015调试sqlserver 存储过程
    C#计算当前日是第几周
    C#计算一年有多少周
    tcpdump命令的使用
    keepalived配置
    正则匹配中文
    git merge 和 git rebase详解
    systemd和sysv服务管理和配置
  • 原文地址:https://www.cnblogs.com/starsing/p/11305731.html
Copyright © 2020-2023  润新知