「データ構造」ボード+セグメントツリーと最大フィールドを計算します

学習セグメントツリーの前提に注目してください

一般的に使用される4つ使用してツリーライン単位シングルポイント修飾 シングルポイントクエリー 修正する間隔 セクション問い合わせを

コードの量が非常に長いですが、アルゴリズムを学習から私たちを防ぐことはできませんでしたことが、そしてコードは、理解しやすいです

アルゴリズムの内容

基本コンセプト

ツリー線BSTの形態と同様1、(バイナリ検索ツリー)詳細の一部ではBSTを区別するために

2、ツリーラインの基本的な考え方:分割統治

図3に示すように、各ノードは、実施形態の構造体に格納され、構造は以下のものを含みます

  • セクションに含まれる現在のノードの左端点、右端点。

  • この間隔を維持し、間隔(または他の間隔と、ここでは焦点を当て)します。

4、彼の父のセグメントツリーノードは、列の数の全範囲のルートである彼の息子と一緒にノードであります

5、ツリーライン用:

  • ツリー内のノード\(K \) プレゼンス\(K \)の子ノードである\(K×2 \)\(。+ 2 1×K \)

  • このノードのための\(K \) その左の子の範囲の間隔\([L、MID] \) その右の息子\([中間+ 1、R ] \)

コードやアイデアのツリーライン

構造

const int N = ???;
struct Node {
    int l, r, w;
} tree[N << 2];

ツリーに開くことを忘れないでください4倍のスペースを

成果、単一のクエリ、単一の点を変更し、クエリ間隔、間隔の変更:セグメントツリーの基本操作は、5を持っています

貢献

アイデア:

  • 各ノード点までの間隔、左右の点所与
  • 各リーフノードのために、我々は彼らに知られている個々の値を与えます
  • セグメントツリーのプロパティを使用して、上向きの合成
void build(int k, int l, int r) {
    tree[k].l = l; tree[k].r = r;
    if(l == r) {
        scanf("%d", &tree[k].w);
        return ;
    }
    
    int mid = (l + r) >> 1;
    build(k * 2, l, mid);
    build(k * 2 + 1, mid + 1, r);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

(ここでは仮定のポイント数は、X、Tの値を追加する必要がある変更する必要がある)の単一のポイントを変更します

アイデア:

  • 我々が探しているの点で示した点を見つけるための間隔は、左のセクションと右のセクションが含まれている場合ので、この点が乗って行きます
  • 私たちは、私たちが消費する必要性のこの部分を見つけ、リーフノード内の特定のポイントを変更したい(O(LOGN)\)\
  • 現在のセクション上記の点につながる可能性が修正点を変更することが含まれているので、このステップは変更することである\(O(LOGN)\)
//Now X and T has been entered;
void change_point(int k) {
    if(tree[k].l == tree[k].r) {
        tree[k].w += t;
        return ;
    }
    
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= x) change_point(k * 2);
    else change_point(k * 2 + 1);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

問い合わせの単一点(照会するように設定ポイントここでxは)

アイデア:

  • そして、同様のシングルポイントの変更、我々は左と右のインターバル間隔を探しているポイントを含む区間で示した点を探しているならば、この時点に乗って行きます
//Now x has been entered;
int ans;
void ask_point(int k) {
    if(tree[k].l == tree[k].r) {
        ans = tree[k].w;
        return ;
    }
    
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= x) ask_point(k * 2);
    else ask_point(k * 2 + 1);
}

クエリ間隔(クエリの範囲セット[L、R])

アイデア:

  • そして、問い合わせの単一点の考え方は似ていますが、ここでは間隔を置き換えます
//Now l and r has been entered
int ans;
void ask_interval(int k, int l, int r) {
    if(tree[k].l >= l && tree[k].r <= r) {
        ans += tree[k].w;
        return ;
    }
    
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= l) ask_interval(k * 2, l, r);
    if(mid < r) ask_interval(k * 2 + 1, l, r);
}

間隔変性(※)

アイデア:

  • 単に私たちはと呼ばれる新しい操作を導入するためにここにいる、見つけて、一定の時間を変更レイジー(レイジー動作)
    • 怠惰な操作あなたはMCサーバプレーとしてのブロックをロードしませんでしたが、どんな行動を持っていませんが、あなただけの行為のいくつかの正常な上、このブロックをロードし、このブロック、上が事実ボックスおよび動物におけるそのような炉の事を燃やすか、苗が育ちます。
    • 怠惰な動作は、実際の一種であるこの点が横断された場合に他の情報と、現在位置に関するその後の情報は、子ノードによって継承され、いくつかの情報は、いくつかのポイントに格納されているイデオロギーのどの子ノードは、この情報にそれは消えません。
//Now l,r,x has been entered
struct Node {
    int l, r, w;
    int f;
} tree[N >> 2];

void Lazydown(int k) {
    tree[k * 2].f += tree[k].f;
    tree[k * 2].w += (tree[k * 2].r - tree[k * 2].l + 1) * tree[k].f;
    tree[k * 2 + 1].f += tree[k].f;
    tree[k * 2 + 1].w += (tree[k * 2 + 1].r - tree[k * 2 + 1].l + 1) * tree[k].f;
    tree[k].f = 0;
}

void change_interval(int k) {
    if(tree[k].l >= l && tree[k].r <= r) {
        tree[k].w += (tree[k].r - tree[k].l + 1) * x;
        tree[k].f += x;
        return ;
    }
    
    if(tree[k].f) Lazydown(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= l) change_interval(k * 2);
    if(mid < r) change_interval(k * 2 + 1);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

注意が必要ないくつかの問題があります。

  • 怠惰な標識構造は、現在のノードに記憶された情報を追加する必要性を変更する必要があります
  • ここでは、変更になる見つけるtree[k].w += (tree[k].r - tree[k].l + 1) * x;あなたが引き分けを確認することができる場所、
  • Lazydown我々は数を通過しなければならないことを意味表し\(k個の\を)それが通過した後に、ポイントであり、確かに情報を渡します
  • Lazydown変更間隔の変更や動作原理は同じで、理解されていない場合は、下の図に得ることができます
  • 私たちが渡され、横断しているため、現在のポイントの情報がクリアされます(そうでなければ、プルバックは(ゲームを言います

入社Lazydown後に、私たちは横断小数点演算が怠惰に行うべきである場合には、すべて私たちは、コードを変更する必要があることを発見しました

次のように完全なコードがあります

//#define fre yes 信仰

#include <cstdio>

const int N = 200005;
struct Node {
    int l, r, w;
    int f;
} tree[N >> 2];

void build(int k, int l, int r) {
    tree[k].l = l; tree[k].r = r;
    if(l == r) {
        scanf("%d", &tree[k].w);
        return ;
    }
    
    int mid = (l + r) >> 1;
    build(k * 2, l, mid);
    build(k * 2 + 1, mid + 1, r);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w
}

void Lazydown(int k) {
    tree[k * 2].f += tree[k].f;
    tree[k * 2].w += (tree[k * 2].r - tree[k * 2].l + 1) * tree[k].f;
    tree[k * 2 + 1].f += tree[k].f;
    tree[k * 2 + 1].w += (tree[k * 2 + 1].r - tree[k * 2 + 1].l + 1) * tree[k].f;
    tree[k].f = 0;
}

void change_point(int k, int x) {
    if(tree[k].l == tree[k].r) {
        tree[k].w += x;
        return ;
    }
    
    if(tree[k].f) Lazydown(k); 
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= l) change_point(k * 2);
    else change_point(k * 2 + 1);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

int ans_p;
void ask_point(int k) {
    if(tree[k].l == tree[k].r) {
        ans_p = tree[k].w;
        return ;
    }
    
    if(tree[k].f) Lazydown(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= l) ask_point(k * 2);
    else ask_point(k * 2 + 1);
}

int ans_i;
void ask_interval(int k, int l, int r) {
    if(tree[k].l >= l && tree[k].r <= r) {
        ans_i += tree[k].w;
        return ;
    }
    
    if(tree[k].f) Lazydown(k);
    int mid = (tree[k].r - tree[k].l) >> 1;
    if(mid >= l) ask_interval(k * 2, l, r);
    if(mid < r) ask_interval(k * 2 + 1, l, r);
}

void change_interval(int k, int l, int r) {
    if(tree[k].l >= l && tree[k].r <= r) {
       tree[k].w += (tree[k].r - tree[k].l + 1) * x;
       tree[k].f += x;
       return ;
    }
    
    if(tree[k].f) Lazydown(k);
    int mid = (tree[k].r - tree[k].l) >> 1;
    if(mid >= l) change_interval(k * 2, l, r);
    if(mid < r) chnage_interval(k * 2 + 1, l, r);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

int main() {
    ...
}

おすすめ

転載: www.cnblogs.com/Nicoppa/p/11411199.html