CodeForces領域193d。二つのセグメント[ツリーライン]

ポータルは、
暴力のその質問には行くことができるものの、この質問が原因で橋カップ2013腐った最後の質問の機会である作るが、ツリーラインアプローチギャング紹介を見て、私はリフレッシュを感じ、私は木のラインのために知っています案の定、ツリーラインは全能であります

問題の意味

あなたは与える\(N- \)完全な配列の\(A \) 次の2つのセクションから選択することができますが、オーバーラップ、二つの部分のすべてが昇順に配置されている場合は、許容範囲ではありません\(1 \)の同じ番号間隔は、プログラムとして、2つの異なるセットが含まれている場合の演算シーケンスが、それは、正当なプログラムです。私はどのように多くの正規のプログラムの合計を求めました。

問題の解決策

それが知られている場合範囲の正の整数を考える([L、R] \ \ ) 元の完全配列内(A \)\に分割されている\(K \) その時点で、ピリオド(。R&LT 1 +を\)\以下の場合には、:

  • もし\(R + 1 \)\([L、R] \ ) 番号のいずれか隣接していない、その後に\([L、R + 1 ] \) に分割されている\(K + 1 \)セクション。
  • 場合\(R + 1 \)\([L、R] \ ) 隣接の数が、その後\([L、R + 1 ] \) 依然としてに分割されている\(K \)セグメント。
  • 場合(R + 1 \)\\([L、R] \ ) 隣接する二つの数字は、次に\([L、R + 1 ] \) に分割される(K-1 \)\セグメント。

この場合、することができます\(O(N ^ 2) \) アルゴリズムは、ここに入りません。データが範囲ため、この問題は、\(N- \ル300 \ 000 \)、\ (O(^ N-2)\)アルゴリズムは許容できません。
しかし、セグメントの上記変化に応じて、我々は、間隔がに、すなわち、変更することができることを見出した(F [L、R] \ \) を表し\([L、R] \ ) 場合、分割するセグメントの数既知の\([1、R&LT] F、F [2、R&LT]、...、F [R&LT、R&LT] \) これらの時間間隔が追加される\(R&LT 1 + \。) 以下の状況:

  • もし(\ [1、R] \ ) のものの数ではない(R + 1 \)\(A \)\次に、隣接:
    \ [F [Iは、R + 1] = F [Iは、R。 ] +1 \]

  • もし\([1、R] \ ) 一つだけ番号が存在する\(X \)\(R + 1 \)\(A \)に隣接する、\([Iは、R + 1](1 \ルI \ルX)\)分割され、セグメントの数がします([I、R]を\ \ ) と同じ(R + 1 \)は\であり、\(X \)強固に固定された♂こと;及び\([I、R + 1 ](x + 1 \ルI \ルR + 1)\) より\([I、R] \ ) によって分割されたセグメントの数\(1 \) その理由は、\([ I、R] \)はカウントされず、\(R + 1 \)隣接し、\(R + 1 \)別のセクションを構成してもよいです。したがって、この場合は以下のように表すことができる
    。。。\ [\開始{整列} F [Iは、R + 1] - = F [I、R&LT]&、&& 1 \ル&I \ルX \\ F [Iは、R + 1] &= F [I、R] +1&、&& X + 1 \ル&I \ルR + 1つの\端{整列} \]

  • もし\([L、R] \ ) 二つの数字があり、\(X、Y(X < Y)\) と\(R + 1 \)\(A \)隣接し、\([ I、R + 1](1 \ルI \ルX)\) 段階の数は分割比で\([I、R] \ ) 以下\(1 \) 元々ため\(x、yは\)分割され、追加\(R + 1 \) 2つのセクションの後に、および\([I、R + 1 ](x + 1 \ルI \ルY)\) と\([I、R] \ )のみから、等しい\(Y \)番号と\(R + 1 \) ;及びペースト\([I、R + 1 \)(Y + 1 \ルI \ルR + 1) の比(\ [I、R] \)マルチ\(1 \) それは次のように表すことができます。
    \ [\ iは、[{整列} F [I、R + 1] - = F [I、R] -1&、&& 1 \ル&I \ルX \\ F [I、R + 1] - = Fを開始R]と、&& X + 1 \ル&I \ルY \\ F [I、R + 1] - = F [I、R] +1&、&& Y + 1 \ル&I \ルR + 1 \端{整列} \]

我々は維持するために、ツリーラインを使用することができるように決定した(r \)\とき(F [I、R] \ )\ 値、質問の意味に従って、各添加\(R + 1 \)のみ必要後は、ツリーラインを照会します値\(1 \)または\(2 \)リスト上のリーフノードの数。

ツリーラインの文言について簡単に説明

なぜなら下(F [L、R] \ \) 特性が知ることができ、そのリーフノードの最小線分\(1 \) 次にチェックする(\ 1)\\(2 \)番号を、のみ最小のメンテナンス間隔と最小値と数値の最小値\(1 + \)リスト上の番号。
注押し上げ操作文言、統計的な範囲の最小値は、最小のサブツリーの最小数についての問い合わせは、具体的には、完全な最小カウントすることを忘れないでください\(+ 1 \)番号を。

コード

#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int N=3e5+10;
int n,a[N],p[N];

struct SegTree{
    #define mid (l+r>>1)
    int num1[N*4],num2[N*4],minv[N*4],tag[N*4];
    void pushdown(int id){
        minv[id<<1]+=tag[id];tag[id<<1]+=tag[id];
        minv[id<<1|1]+=tag[id];tag[id<<1|1]+=tag[id];
        tag[id]=0;
    }
    void pushup(int id){
        minv[id]=min(minv[id<<1],minv[id<<1|1]);
        num1[id]=num2[id]=0;
        if(minv[id<<1]==minv[id]) num1[id]+=num1[id<<1],num2[id]+=num2[id<<1];
        else if(minv[id<<1]==minv[id]+1) num2[id]+=num1[id<<1];
        if(minv[id<<1|1]==minv[id]) num1[id]+=num1[id<<1|1],num2[id]+=num2[id<<1|1];
        else if(minv[id<<1|1]==minv[id]+1) num2[id]+=num1[id<<1|1];
    }
    void build(int id,int l,int r){
        num1[id]=r-l+1;
        if(l==r) return;
        build(id<<1,l,mid);
        build(id<<1|1,mid+1,r);
    }
    void upd(int id,int l,int r,int L,int R,int x){
        if(L<=l&&r<=R) {minv[id]+=x;tag[id]+=x;return;}
        if(tag[id]) pushdown(id);
        if(L<=mid) upd(id<<1,l,mid,L,R,x);
        if(R>mid) upd(id<<1|1,mid+1,r,L,R,x);
        pushup(id);
    }
    int ask(int id,int l,int r,int L,int R){
        if(L<=l&&r<=R) {
            if(minv[id]==1) return num1[id]+num2[id];
            if(minv[id]==2) return num1[id];
            return 0;
        }
        if(tag[id]) pushdown(id);
        int res=0;
        if(L<=mid) res+=ask(id<<1,l,mid,L,R);
        if(R>mid) res+=ask(id<<1|1,mid+1,r,L,R);
        return res;
    }
    #undef mid
}tr;

int main(){
    scanf("%d",&n);
    for(int i=1,x;i<=n;i++) scanf("%d",&x),p[x]=i;
    LL ans=0;
    tr.build(1,1,n);
    for(int i=1;i<=n;i++){
        a[p[i]]=i;
        int x=a[p[i]-1],y=a[p[i]+1];
        if(x>y) swap(x,y);
        if(x) tr.upd(1,1,n,1,x,-1),tr.upd(1,1,n,y+1,i,1);
        else if(y) tr.upd(1,1,n,y+1,i,1);
        else tr.upd(1,1,n,1,i,1);
        ans+=tr.ask(1,1,n,1,i);
        //cout<<"->"<<ans<<endl;
    }
    ans-=n;
    cout<<ans<<endl;
    return 0;
}

おすすめ

転載: www.cnblogs.com/BakaCirno/p/12466819.html