1. 程式人生 > >HDU 1754 I Hate It(線段數-單點更新)

HDU 1754 I Hate It(線段數-單點更新)

原題連結:I Hate It

題目分析:這個是線段樹的基本操作——更新和查詢,屬於基礎題,也就相當於一個模板吧,這裡就不再多說了。


程式碼如下:
(PS:特別注意——輸入輸出用 scanf()\,\,printf() 不然會超時)

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;

const int MAXNODE = 524288/*2097152*/;
const int MAX = 200001/*1000003*/;

struct NODE {
    int value;        // 結點對應區間的權值
    int left,right;   // 區間 [left,right]
} node[MAXNODE];

int father[MAX];     // 每個點(當區間長度為0時,對應一個點)對應的結構體陣列下標

void BuildTree(int i, int left, int right) { // 為區間[left,right]建立一個以i為祖先的線段樹,i為陣列下標,我稱作結點序號
    node[i].left = left;	// 寫入第i個結點中的 左區間
    node[i].right = right;	// 寫入第i個結點中的 右區間
    node[i].value = 0; 		// 每個區間初始化為 0

    if (left == right) {	// 當區間長度為 0 時,結束遞迴
        father[left] = i;   // 能知道某個點對應的序號,為了更新的時候從下往上一直到頂
        return;
    }

    // 該結點往 左孩子的方向 繼續建立線段樹,線段的劃分是二分思想,如果寫過二分查詢的話這裡很容易接受
    // 這裡將 區間[left,right] 一分為二了
    BuildTree(i << 1, left, (int)floor((right + left) / 2.0));
    // 該結點往 右孩子的方向 繼續建立線段樹
    BuildTree((i << 1) + 1, (int)floor((right + left) / 2.0) + 1, right);
}

void UpdataTree(int ri) {    // 從下往上更新(注:這個點本身已經在函式外更新過了)

    if (ri == 1) return;	 // 向上已經找到了祖先(整個線段樹的祖先結點 對應的下標為1)
    int fi = ri / 2; 		 // ri 的父結點
    int a = node[fi << 1].value;            // 該父結點的兩個孩子結點(左)
    int b = node[(fi << 1) + 1].value;      // 右
    node[fi].value = (a > b) ? (a) : (b);	// 更新這個父結點(從兩個孩子結點中挑個大的)
    UpdataTree(ri / 2);	        	        // 遞迴更新,由父結點往上找
}

int Max /*= -1 << 20*/;
void Query(int i, int l, int r) {                     // i為區間的序號(對應的區間是最大範圍的那個區間,也是第一個圖最頂端的區間,一般初始是 1 啦)
    if (node[i].left == l && node[i].right == r) {    // 找到了一個完全重合的區間
        Max = (Max < node[i].value) ? node[i].value : (Max);
        return;
    }

    i = i << 1;                                       // get the left child of the tree node
    if (l <= node[i].right) {                         // 左區間有涉及
        if (r <= node[i].right)                       // 全包含於左區間,則查詢區間形態不變
            Query(i, l, r);
        else                                          // 半包含於左區間,則查詢區間拆分,左端點不變,右端點變為左孩子的右區間端點
            Query(i, l, node[i].right);
    }
    i += 1;                                           // right child of the tree
    if (r >= node[i].left) {                          // 右區間有涉及
        if (l >= node[i].left)                        // 全包含於右區間,則查詢區間形態不變
            Query(i, l, r);
        else                                          // 半包含於左區間,則查詢區間拆分,與上同理
            Query(i, node[i].left, r);
    }
}

int main() {
    int n, m, Value;

    while (scanf("%d %d", &n, &m) != EOF) {
        BuildTree(1, 1, n);

        for (int i = 1; i <= n; i++) {
            scanf("%d", &Value);
            node[father[i]].value = Value;            // 底層的無條件更新成績
            UpdataTree(father[i]);
        }

        while (m--) {
            char ch[2];
            int a, b;

            scanf("%s %d %d", ch, &a, &b);
            if (ch[0] == 'Q') {
                Max = 0;
                Query(1, a, b);
                printf("%d\n", Max);
            } else {
                node[father[a]].value = b;            // 底層的無條件更新成績
                UpdataTree(father[a]);
            }
        }
    }

    return 0;
}