zoukankan      html  css  js  c++  java
  • CF 997E 解题报告

    CF997E Good Subsegments

    给你一个长度为(n)的排列 (P),定义一段子区间是好的,当且仅当这个子区间内的值构成了连续的一段。例如对于排列({1,3,2})([1,1],[2,2],[3,3],[2,3],[1,3])是好的区间。
    (q)次询问,每次询问(L,R), 求有多少(L≤l≤r≤R),满足([l,r])是好的区间。(1≤n,q≤1.2×10^5).


    积累一个套路:对于询问区间子序列的信息,可以离线移动右指针,类似扫描线一样计算贡献。

    注意到若区间([l,r])满足(max-min=r-l),则这个区间是好的区间。

    于是在当前指针(r)下维护一个((max-min)-(r-l))的最小值以及最小值个数。

    然后维护一个答案(sum),代表某个区间最小值的贡献(注意这时候不需要维护是不是(0),我们只需要在最后累计答案的时候在树根判断就可以了,这个题根一定为(0),所以没有判)

    但是发现这样不行,我们只是维护了一个右端点为(r)的区间的贡献,于是维护一个(dev)贡献数组,维护历史出现的最小值出现的贡献。

    具体点说,(dev)也相当于一个(tag),表示这个区间当前的答案需要被贡献到(sum),不管它之后怎么样。

    然后区间(max,min)的相关东西用单调栈额外维护,复杂度是均摊的(log n)

    一次操作的大体流程:

    1. 对整个区间打一个右移标记,即区间最小值减(1)
    2. 维护单调栈并更新一下(max,min)
    3. 打上历史更新(tag)
    4. 进行询问

    一些细节:初始时每个区间的(mi)是它自己的左端点,因为每次是对整颗树的区间打的标记,会减去很多次(1),下放标记的时候更新答案看是不是跟自己一样,如果子区间最小值跟自己一样才进行更新。


    Code:

    #include <cstdio>
    #include <algorithm>
    #define ll long long
    const int N=120010;
    using std::min;
    int mi[N<<2],ct[N<<2],mitg[N<<2],dev[N<<2];
    //最小值,最小值次数,最小值tag,历史贡献tag
    ll ans[N],sum[N<<2];
    int sta1[N],tot1,sta2[N],tot2;
    int n,m,p[N];
    struct node
    {
        int id,l,r;
        bool friend operator <(node n1,node n2){return n1.r<n2.r;}
    }q[N];
    #define ls id<<1
    #define rs id<<1|1
    void build(int id,int l,int r)
    {
        mi[id]=l,ct[id]=1;int mid=l+r>>1;
        if(l^r) build(ls,l,mid),build(rs,mid+1,r);
    }
    void addmi(int id,int d){mi[id]+=d,mitg[id]+=d;}
    void addde(int id,int d){sum[id]+=1ll*ct[id]*d,dev[id]+=d;}
    void updata(int id)
    {
        mi[id]=min(mi[ls],mi[rs]),ct[id]=0;
        ct[id]=mi[id]==mi[ls]?ct[id]+ct[ls]:ct[id];
        ct[id]=mi[id]==mi[rs]?ct[id]+ct[rs]:ct[id];
        sum[id]=sum[ls]+sum[rs];
    }
    void pushdown(int id)
    {
        if(mitg[id]) addmi(ls,mitg[id]),addmi(rs,mitg[id]),mitg[id]=0;
        if(dev[id])
        {
            if(mi[id]==mi[ls]) addde(ls,dev[id]);
            if(mi[id]==mi[rs]) addde(rs,dev[id]);
            dev[id]=0;
        }
    }
    void change(int id,int L,int R,int l,int r,int d)
    {
        if(l==L&&r==R){addmi(id,d);return;}
        int Mid=L+R>>1;pushdown(id);
        if(r<=Mid) change(ls,L,Mid,l,r,d);
        else if(l>Mid) change(rs,Mid+1,R,l,r,d);
        else change(ls,L,Mid,l,Mid,d),change(rs,Mid+1,R,Mid+1,r,d);
        updata(id);
    }
    ll query(int id,int L,int R,int l,int r)
    {
        if(l==L&&r==R)return sum[id];
        int Mid=L+R>>1;pushdown(id);
        if(r<=Mid) return query(ls,L,Mid,l,r);
        else if(l>Mid) return query(rs,Mid+1,R,l,r);
        else return query(ls,L,Mid,l,Mid)+query(rs,Mid+1,R,Mid+1,r);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",p+i);
        scanf("%d",&m);
        for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
        std::sort(q+1,q+1+m);
        build(1,1,n);
        for(int j=1,i=1;i<=n;i++)
        {
            addmi(1,-1);
            while(tot1&&p[sta1[tot1]]<=p[i])
            {
                change(1,1,n,sta1[tot1-1]+1,sta1[tot1],p[i]-p[sta1[tot1]]);
                --tot1;
            }
            sta1[++tot1]=i;
            while(tot2&&p[sta2[tot2]]>=p[i])
            {
                change(1,1,n,sta2[tot2-1]+1,sta2[tot2],p[sta2[tot2]]-p[i]);
                --tot2;
            }
            sta2[++tot2]=i;
            addde(1,1);
            while(q[j].r==i) ans[q[j].id]=query(1,1,n,q[j].l,q[j].r),++j;
        }
        for(int i=1;i<=m;i++) printf("%lld
    ",ans[i]);
        return 0;
    }
    

    2018.12.25

  • 相关阅读:
    calendar.getTimeInMillis() 和 System.currentTimeMillis() 的区别
    微信小程序中使用 <web-view> 内嵌 H5 时,登录问题的处理方法
    小程序 TabBar 定制
    webpack 代码优化压缩方法
    react-router v4 按需加载的配置方法
    axios发送post请求,如何提交表单数据?
    react中键盘enter事件处理
    常用证件正则表达式
    react中input自动聚焦问题
    React Router v4 页面传值的三种方法
  • 原文地址:https://www.cnblogs.com/butterflydew/p/10176314.html
Copyright © 2011-2022 走看看