グッド魔法のデータ構造うん...
まず、我々は考える:暴力はどうすればよいですか?
範囲の限界位置は$ J $ $ [L、R] $である場合、状態$のDPをリセットする[J] $ $ J $が最後の数最小のシーケンス番号の長さを示し、は、明らかに転移。
$ DP [J] = L(DP [J-1] <L)$
$ DP [j]はDPを= [J-1] +1(L \当量DP [J-1] \当量のR-1)$
$ DP [J] = DP [J](DP [J-1] \ GEQ R)$
明らかに正し
最適化を考えてみましょう:私たちは、上記の3つの操作の物質を考慮してください。
最初の転送方向:前駆体の後ろにL $ $ $ L $を挿入します
二次転写方向:間隔の$ [Lを、R-1〕$プラス全体、右のセルの後に(後方にずれた位置に対応します)
第3の転送方向:削除の$ $のR&LT後継
そして、バランスの取れたツリーを使用して直接、考慮に挿入作業を取るたびに、上記の3つの操作を実装することができますので、右の第2の操作は、単に追加するという本当の事実を必要としません。
強くfhq_treapを達成することをお勧めします
コード:
書式#include <cstdioを> する#include <cmath> の#include <CStringの> の#include <cstdlib> 書式#include <iostreamの> の#include <アルゴリズム> 書式#include <キュー> の#include <スタック> の#include <ctimeの> に#define LSツリー[ RT] .lson の#define RSツリー[RT] .rson 使用して 名前空間はstdを、 構造体FHQ_TREAP { int型LSON、rson。 int型のSIZ。 int型のval、ランク; int型怠惰; }ツリー[ 300005 ]。 int型 TOT =0 ; int型の腐敗、再、N。 ボイド押し上げ(INT RT) { ツリー[RT] .siz =ツリー[LS] .siz +ツリー[RS] .siz + 1 。 } 無効プッシュダウン(int型RT) { 場合(!RT)のリターン; もし(LS)ツリー[LS] .lazy + =ツリー[RT] .lazy、ツリー[LS] .val + = ツリー[RT] .lazy。 もし(RS)ツリー[RS] .val + =ツリー[RT] .lazy、ツリー[RS] .lazy + = ツリー[RT] .lazy。 ツリー[RT] .lazy = 0 。 } INT new_node(INT V) { int型 RET = ++TOT; ツリー[RET] .siz = 1、ツリー[RET] .val = V、ツリー[RET] .rank = ランド()。 リターンRET; } INTマージ(int型のx、int型Y) { 場合(!(X)||(Y))リターン X | Y; もし(ツリー[X] .lazy)プッシュダウン(X)。 もし(ツリー[Y] .lazy)プッシュダウン(Y)。 もし(ツリー[X] .rank < ツリー[Y] .rank) { ツリー[X] .rson = (ツリー[X] .rson、y)をマージします。 突き上げ(X)。 リターンのx; } 他 { ツリー[Y] .lson= (X、ツリー[Y] .lson)をマージします。 突き上げ(Y)。 返すyと。 } } ボイド分割(INT RT、INT K、INT&X、INT&Y) { 場合(RT!){X = Y = 0。返す;} もし(ツリー[RT] .lazy)プッシュダウン(RT)。 もし(ツリー[RT] .val <= K)X = RT、スプリット(RS、K、RS、Y)。 そうでなければ、Y = RT、スプリット(LS、K、X、LS)。 突き上げ(RT)。 } ボイドイン(INT V) { int型X、Y。 スプリット(腐敗、V、X、Y)。 腐敗 =。マージ((X、new_node(V))、Yマージ)。 } 無効デル(INT V) { int型X、Y、Z、W。 スプリット(腐敗、V、X、Y)。 スプリット(X、V - 1 、Z、W)。 W = (.lson、ツリー[W] .rson [W]ツリー)をマージします。 腐敗 = (W Y)、(Zマージ)マージ。 } ボイド更新(int型 RT、int型の L、INT R) { int型X、Y、Z、W。 スプリット(腐敗、L - 1 、X、Y) スプリット(Y、R、Z、W)。 もし(Z)ツリー[Z] .lazy ++、ツリー[Z] .val ++ 腐敗 =マージ(X(Z、W)をマージ)。 } 無効 get_pro(int型 RT、int型V) { 場合(!RT)のリターン; 場合(V <= ツリー[RT] .val)get_pro(LS、V)。 他再= ツリー[RT] .val、get_pro(RS、V)。 } INT get_minn(INT RT) { 場合(LS!)戻りツリー[RT] .val。 他の リターンget_minn(LS); } INT get_sub(INT V) { int型X、Y。 スプリット(腐敗、V、X、Y)。 int型 RET =get_minn(Y)。 腐敗 = (x、y)をマージします。 リターンRET; } インラインint型リード() { int型、F = 1、X = 0。チャー CH = GETCHAR()。 一方、(CH < ' 0 ' || CH> ' 9 '){ 場合(CH == ' - ')、F = - 1 ; CH = GETCHAR();} 一方(CH> = ' 0 ' && CH <= ' 9 '){X = X * 10CH- + イン(L)。' 0 ' ; CH = GETCHAR();} 戻りのx *のF。 } ボイド)(解く { srand関数(時間(NULL))。 N = 読み取ります(); 以下のために(int型 i = 1 ; iが<= N; iは++ ) { int型 L =読み取る()、R = 読み取り()。 もし(I == 1){イン(L)。引き続き;} 再 = get_sub(R- 1 )。 アップデート(腐敗、L、R - 1 )。 もし(再)デル(再)。 } 場合(!腐れ病)のprintf(" 0 \ nを" ); 他のprintf(" %d個の\ n " 、ツリー[腐敗] .siz)。 } int型のmain() { )(解きます。 リターン 0 ; }