私は今、データ構造を常習していますので、データ構造は、何よりもはるかに興味深いと思い...
正题:
木の会長
また、持続可能な技術ツリーラインとして知られている、(実際には、順番に、最終的にはこの問題について話すべきである[元首])
ツリーラインを把握することをお勧めします
いわゆる永続缶は、定義により、つまり、「永続的」であります長い時間を実行しています、
歴史のない、操作がサポートされているバージョン、
栗の場合:
列の数は今与えられた\(A \) 、およびいくつかの修正単一の要素、
各変更は、各変更は、新しいシリーズを生成するように理解することができるバージョンを生成します
それぞれの変更はまた、最後にもう一つの可能な変更に変更された別のバージョンに基づいています
(実際には、おそらく元の配列の上に、実際には、「0」のバージョンを変更し)、
いくつかのインターバル与える次いで\([L、R] \ ) 及び(保証有効)非負整数\(kは\) 、出力は、いくつかの配列情報が必要になり、k番目の修正版であり、それは単一であってもよいです点要素の値、およびそれが範囲とすることができる、等が可能な最大および最小の試験
間隔の長さと操作の数など、そのようテンプレートのタイトルテンプレートのタイトル名は「永続的な配列が、それは真美実際には本当に持続セグメントの木」ここ
\(N、M \ leqslant 1000000 \)
この出会いは、私たちが必要とする、がんや困難な問題を抱えていますそれの顔を笑顔暴力はまず、正の解を分析し、次に考えました
話題のかなりの部分では、我々はできる暴力の考えに基づいて正の解を起動します
アイデアや原則の基本的な実現
だから、最も基本的に基づいてみましょう問い合わせのシングルポイント反射のために
最も暴力的な方法について考え、この質問を参照してください。
オープン\(1000000 * 1000000 \)ストレージアレイの暴力の
どうやらだけでとってもキュート暴力的な格差を取ります...
第二に、我々は少しの最適化を検討することができます。
オープンダイナミックアレイ(ベクトル)\(A [N-] \) 、加算値に対応する位置に各動作は、クエリを直接することができ
ビットの平均値は、これは、戦略の比較優位であるべきです
しかし、これは変更されたアプローチに基づいて操作を変更しません、
修正したいだけの調査に従事することができる場合は、操作のセットなどを示し、
しかし、これは長いったらしいで悩み、あります...
その後、他のために必要な保守間隔情報やお問い合わせは、それ?
上記動作範囲のために、簡単に見るために、セグメントツリーを係合し、
時間の複雑さをよりよく確保するので、我々は唯一、このようなツリーラインなどの強力なデータ構造、の運転範囲の様々なサポートするために選択することができます
この暴力を考えてみましょう:
各操作は、すべての情報セクションを維持するために、新しいツリーを構築するために
バーストスペースが、少なくとも\(O [(1E6)^ 2 * 4] \)
しかし、これはすべての後に、間違いなく正しいアプローチであるので、あなたは正しい間隔情報を維持することができます...
我々は、クエリ上記の単一ポイントとしてできないいくつかの最適化、それ?
いくつかは、アカウントに特別な性質を取ることはありません考えてみましょう:
- 各変更は変更のみ一つの要素を、
言い換えれば、我々は列の数を変更することなく、それぞれの変更のほとんどを持っています
、セクション情報のために、私たちは修正に基づいて他の要素を使用して変更することはできませんされていること、
我々は、次のアイデアを検討してください。
彼は、最も初期シーケンス対応するセグメントツリーとして、ツリーラインを構築し始めました
その後、各変更のために、我々は、範囲の新たに改訂されたバージョンに対応する修飾された要素として、より少ない新しいノードを作成、改変エレメントセクションを変更する必要があります、
バージョンに基づいて、それぞれの変更は、間隔を変更しなかった人のために、我々はそれを処理するために何を裏返しする必要が新しいノードとの間の関係についての息子は、それが修正されることなく、素子間隔との接触を確立するため、
言い換えれば、私たちは、古いノードとの接続を確立するために新しいノードを置く部分が改訂として、変更する必要はありません
その結果、我々はこのように大幅に時間の複雑さを低減し、再使用の前にオーバープルする要素を変更することはできません
理論 - 関数の実装
ここでは主なメンテナンスシングルポイントの値は、メンテナンス間隔値の単一のポイントの価値は何もありません学びました
ボード上のこの質問:真会長ツリーテンプレートのタイトル
アイデアを、どのように我々はそれを達成していますか?
一時的に過去を忘れて、すべての最初のヒープ・ストレージ・セグメントツリーは、そのようなお店です。
PSここでは言葉は、彼らがしていることが明らかにされていません\(K << 1 \) 、\(| 1 \ K << 1)
私たちはノードの息子の前に新しいノードを行うためにオーバープルする必要があるかもしれないので、すべてのノードのツリー番号の社長が、混乱しているので、それは十分に確立されていません
- 必要な変数を見てください:
int rt[N<<5];
struct node{
int ls,rs,val;
}nd[N<<5];
int cnt;
int a[N];
\(CNT \)ポイント、スタックに似た役割を開くために使用された\(トップ\)ハン、あなたが新しいノードを作成するたびに\(++ CNT \)
構造のために(ND \)\要素において、\(LS、RS \)息子程度で、\(ヴァル\)ノードの重みの範囲は、素子の値の一点です
\(\)の配列は、元の配列であります
ポイントコードは、新しい外観の結合様式:
- 最初の成果の機能\(ビルド\)
inline int build(ci l,ci r){
int k=++cnt;
if(l==r){nd[k].val=a[l];return k;}
int mid=l+r>>1;
nd[k].ls=build(l,mid);
nd[k].rs=build(mid+1,r);
return k;
}
ここでは通常の寄与が毎回同じ場所ではありません\(ビルド\)関数は値を返し、この値は、親子関係を処理するため、新たなノードの数であります
、現在のノード番号が上がる返す父親が息子のために確立させることです
成果については、重量のみの単一ポイントのメンテナンスの対象は、非リーフノードが保持\(ヴァル\)意味がありませんが
だから我々はそれを変更する方法に対処する必要がありますか?
この木考えてみましょう:
以上で(ビルド\)\関数は、幽霊が出て構築され、
我々は配列要素を変更する場合は、\(A [3] \) 、グラフの対応するノード\(6 \) 、
我々は、封入ノードことが判明\(6 \) 、全ての対応する要素のノード(ツリー線間隔)が、その親であります
私たちは、親ノードのそれぞれに新しいノードを作成して、元の要素は、利用します
このように:
括弧内の元のノードを表し、
だから我々は3超小型スペースでのノードの動作に巨大な木のトラブルの再構築を完了しなければなりませんでした
コードを見てください:
- 機能変更\(挿入\)
inline int insert(ci k,ci l,ci r,ci x,ci v){
int nk=++cnt;
nd[nk]=nd[k];
if(l==r){nd[nk].val=v;return nk;}
int mid=l+r>>1;
if(x<=mid) nd[nk].ls=insert(nd[nk].ls,l,mid,x,v);
else nd[nk].rs=insert(nd[nk].rs,mid+1,r,x,v);
return nk;
}
(ビルド\)\関数はほとんど、それは、コピーノード操作以上のものです
ここでの原則は、元のノードの新しいノードのバージョンをコピーし、新しい値を割り当て、新しい息子を作成することです、
- クエリ機能\(クエリ\)
特定の値の特定のバージョンに戻り、
inline int query(ci k,ci l,ci r,ci x){
if(l==r) return nd[k].val;
int mid=l+r>>1;
if(x<=mid) return query(nd[k].ls,l,mid,x);
else return query(nd[k].rs,mid+1,r,x);
}
このような基本的な機能は、基本的には、一部を終え、
完全なコード
#include<iostream>
#include<cstdio>
#define ci const int & //can you follow my speed?
using namespace std;
const int N=1000005;
int n,m;
int rt[N<<5];
struct node{
int ls,rs,val;
}nd[N<<5];
int cnt;
int a[N];
inline int build(ci l,ci r){
int k=++cnt;
if(l==r){nd[k].val=a[l];return k;}
int mid=l+r>>1;
nd[k].ls=build(l,mid);
nd[k].rs=build(mid+1,r);
return k;
}
inline int insert(ci k,ci l,ci r,ci x,ci v){
int nk=++cnt;
nd[nk]=nd[k];
if(l==r){nd[nk].val=v;return nk;}
int mid=l+r>>1;
if(x<=mid) nd[nk].ls=insert(nd[nk].ls,l,mid,x,v);
else nd[nk].rs=insert(nd[nk].rs,mid+1,r,x,v);
return nk;
}
inline int query(ci k,ci l,ci r,ci x){
if(l==r) return nd[k].val;
int mid=l+r>>1;
if(x<=mid) return query(nd[k].ls,l,mid,x);
else return query(nd[k].rs,mid+1,r,x);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
rt[0]=build(1,n);
for(int i=1;i<=m;i++){
int ver,opr,loc;
scanf("%d%d%d",&ver,&opr,&loc);
if(opr==1){
int v;
scanf("%d",&v);
rt[i]=insert(rt[ver],1,n,loc,v);
} else {
rt[i]=rt[ver];
printf("%d\n",query(rt[i],1,n,loc));
}
}return 0;
}
トピック特殊性
そのテンプレートこのタイトルのためにごみ無類操作は比較的単純な(問い合わせの一点のみ)問題であり、彼らはこのような単純なが発生することはありません
これは問い合わせ話題の一点のみを必要とするためしかし、我々は、一連の操作を使用すると言うことができ、
その他の変更の上に構築され、現在の修正後、このシーケンスは、複数の値が変更されているがあり、ではなく、単一のベクターを解消することができるので
互いに素-セットが変更の束の集合体である限り、この質問を行うことができます理由です生産し、その上に
遂に
これは、ツリーの基本的な会長であります
私は良いか悪いかああ、それを更新するというように、後で戻ってくると言います...
このデータ構造の発明について、彼は自分の感覚、黄Jiataiの略語HJTと呼ばれます