zoukankan      html  css  js  c++  java
  • Codeforces 193 D. Two Segments

    http://codeforces.com/contest/193/problem/D

     

    题意:

    给一个1~n的排列,在这个排列中选出两段区间,求使选出的元素排序后构成公差为1的等差数列的方案数。

     

    换个角度思考问题,题意转化为存在多少对[L,R] ,(R>L),满足将值为[L,R]的区间染色后,所得区间数<=2

    假设现在已知[L,R]的染色情况,看将值为L-1的位置染色后,区间数量的变化

    若L-1左右两边都没有染色,区间数量+1

    若L-1左右两边有一边染了色,区间数量不变

    若L-1左右两边都染色了,区间数量-1

    这样就有了枚举L R 的 n^2 做法

     

    令f[i]表示当左端点为L,有端点为i时区间的数量

    从大到小枚举L

    考虑由[L,m]   m∈[L+1,n]  到 [L-1,m]  m∈[L,n] 时

    f[i] i∈[L-1,n]的变化

    设L-1 左右两边的数分别为x和y,且x<y

    A、L-1左右两边都没有染色,即x<y<L-1,

    染上L-1后会使区间数+1,即f[i]加1 ,i∈[L-1,n]

    B、L-1左右两边有一边染色,即x<L-1<y,(y的那一边染色)

    若染色的区间原本不包含y,染上L-1后会使区间数+1,即f[i]加1,i∈[L-1,y-1]

    若染色的区间原本包含y,L-1与y相连,染上L-1后区间数不变

    C、L-1左右两边都染色了,即L-1<x<y

    若染色的区间原本不包含x,也不包含y,染上L-1后会使区间数+1,即f[i]加1,i∈[L-1,x-1]

    若染色的区间原本只包含其中一个(只包含y),染山L-1后区间数不变

    若染色的区间原本包含x和y,染上L-1后,两边的区间相连,区间数-1,即f[i]减1,i∈[y,n]

    用线段树维护f[i]

    这就变成了线段树的区间+1,区间-1,查询区间内f[i]<=2的数的个数

    维护区间最小值mi[i],等于最小值的个数tot[i],等于最小值+1的个数tot1[i]

    答案由两部分组成:

    1、mi[i]<=2,ans+=tot[i]

    2、mi[i]==1,ans+=tot1[i]

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 300001
    
    int a[N+1],b[N+1];
    
    int tag[N<<2];
    int tot[N<<2],tot1[N<<2];
    int mi[N<<2];
    
    long long ans=0;
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void build(int k,int l,int r)
    {
        tot[k]=r-l+1;
        if(l==r) return;
        int mid=l+r>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
    
    void down(int k)
    {
        tag[k<<1]+=tag[k];
        tag[k<<1|1]+=tag[k];
        mi[k<<1]+=tag[k];
        mi[k<<1|1]+=tag[k];
        tag[k]=0;
    }
    
    void update(int k)
    {
        mi[k]=min(mi[k<<1],mi[k<<1|1]);
        tot[k]=tot[k<<1]*(mi[k<<1]==mi[k])+tot[k<<1|1]*(mi[k<<1|1]==mi[k]);
        tot1[k]=tot1[k<<1]*(mi[k<<1]==mi[k])+tot1[k<<1|1]*(mi[k<<1|1]==mi[k]);
        tot1[k]+=tot[k<<1]*(mi[k<<1]==mi[k]+1)+tot[k<<1|1]*(mi[k<<1|1]==mi[k]+1);
    }
    
    void change(int k,int l,int r,int opl,int opr,int w)
    {
        if(l>=opl && r<=opr)
        {
            mi[k]+=w;
            tag[k]+=w;
            return;
        }
        if(tag[k]) down(k);
        int mid=l+r>>1;
        if(opl<=mid) change(k<<1,l,mid,opl,opr,w);
        if(opr>mid) change(k<<1|1,mid+1,r,opl,opr,w);
        update(k);
    }
    
    void query(int k,int l,int r,int opl,int opr)
    {
        if(l>=opl && r<=opr)
        {
            ans+=tot[k]*(mi[k]<=2)+tot1[k]*(mi[k]==1);
            return;
        }
        if(tag[k]) down(k);
        int mid=l+r>>1;
        if(opl<=mid) query(k<<1,l,mid,opl,opr);
        if(opr>mid) query(k<<1|1,mid+1,r,opl,opr);
    }    
    
    int main()
    {
        int n,m;
        read(n);
        int x,y;
        for(int i=1;i<=n;++i) read(x),a[x]=i;
        build(1,1,n);
        for(int i=n;i;--i)
        {
            b[a[i]]=i;
            x=b[a[i]-1];
            y=b[a[i]+1];
            if(x>y) swap(x,y);
            if(x)
            {
                change(1,1,n,y,n,-1);
                change(1,1,n,i,x-1,1);
            }
            else if(y) change(1,1,n,i,y-1,1);
            else change(1,1,n,i,n,1);
            //long long last=ans;
            query(1,1,n,i,n);
            //cout<<i<<' '<<ans-last<<'
    ';
        }
        cout<<ans-n;
    }
  • 相关阅读:
    SQL 运算符
    Shiro 入门
    SSM 整合配置
    MyBatis 入门
    Git 常用命令
    JSP
    Servlet
    Oracle 基础
    JDBC
    Java Thread
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8444650.html
Copyright © 2011-2022 走看看