bzoj2049 [Sdoi2008]Cave 洞穴勘測 (Link Cut Tree)

Description

  • 輝輝熱衷於洞穴勘測。某天,他按照地圖來到了一片被標記爲JSZX的洞穴羣地區。經過初步勘測,輝輝發現這片區域由n個洞穴(分別編號爲1到n)以及若干通道組成,並且每條通道連接了恰好兩個洞穴。假如兩個洞穴可以通過一條或者多條通道按一定順序連接起來,那麼這兩個洞穴就是連通的,按順序連接在一起的這些通道則被稱之爲這兩個洞穴之間的一條路徑。洞穴都十分堅固無法破壞,然而通道不太穩定,時常因爲外界影響而發生改變,比如,根據有關儀器的監測結果,123號洞穴和127號洞穴之間有時會出現一條通道,有時這條通道又會因爲某種稀奇古怪的原因被毀。輝輝有一臺監測儀器可以實時將通道的每一次改變狀況在輝輝手邊的終端機上顯示:如果監測到洞穴u和洞穴v之間出現了一條通道,終端機上會顯示一條指令 Connect u v 如果監測到洞穴u和洞穴v之間的通道被毀,終端機上會顯示一條指令 Destroy u v 經過長期的艱苦卓絕的手工推算,輝輝發現一個奇怪的現象:無論通道怎麼改變,任意時刻任意兩個洞穴之間至多隻有一條路徑。因而,輝輝堅信這是由於某種本質規律的支配導致的。因而,輝輝更加夜以繼日地堅守在終端機之前,試圖通過通道的改變情況來研究這條本質規律。然而,終於有一天,輝輝在堆積成山的演算紙中崩潰了……他把終端機往地面一砸(終端機也足夠堅固無法破壞),轉而求助於你,說道:“你老兄把這程序寫寫吧”。輝輝希望能隨時通過終端機發出指令 Query u v,向監測儀詢問此時洞穴u和洞穴v是否連通。現在你要爲他編寫程序回答每一次詢問。已知在第一條指令顯示之前,JSZX洞穴羣中沒有任何通道存在。

Input

  • 第一行爲兩個正整數n和m,分別表示洞穴的個數和終端機上出現過的指令的個數。以下m行,依次表示終端機上出現的各條指令。每行開頭是一個表示指令種類的字符串s(”Connect”、”Destroy”或者”Query”,區分大小寫),之後有兩個整數u和v (1≤u, v≤n且u≠v) 分別表示兩個洞穴的編號。

Output

  • 對每個Query指令,輸出洞穴u和洞穴v是否互相連通:是輸出”Yes”,否則輸出”No”。(不含雙引號)

Sample Input

  • 樣例輸入1 cave.in
    200 5
    Query 123 127
    Connect 123 127
    Query 123 127
    Destroy 127 123
    Query 123 127
  • 樣例輸入2 cave.in
    3 5
    Connect 1 2
    Connect 3 1
    Query 2 3
    Destroy 1 3
    Query 2 3

Sample Output

  • 樣例輸出1 cave.out
    No
    Yes
    No

  • 樣例輸出2 cave.out
    Yes
    No


HINT

  • 數據說明 10%的數據滿足n≤1000, m≤20000 20%的數據滿足n≤2000, m≤40000 30%的數據滿足n≤3000, m≤60000 40%的數據滿足n≤4000, m≤80000 50%的數據滿足n≤5000, m≤100000 60%的數據滿足n≤6000, m≤120000 70%的數據滿足n≤7000, m≤140000 80%的數據滿足n≤8000, m≤160000 90%的數據滿足n≤9000, m≤180000 100%的數據滿足n≤10000, m≤200000 保證所有Destroy指令將摧毀的是一條存在的通道本題輸入、輸出規模比較大,建議c\c++選手使用scanf和printf進行I\O操作以免超時

題解

再熟悉一下模板吧。。
這裏說一下如何判斷兩個點是否在一棵子樹:對於一個點x,先access(x),再splay(x),然後返回x左邊最小的點,用它來表示x所在的子樹:

int find(int x) {
    access(x);
    splay(x);
    while (tr[x][0]) x = tr[x][0];
    return x;
}

剩下就只有link和cut操作了。。話說lct的模板算是很好打的了。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <climits>

using namespace std;

const int MAXN = 10005;

int n, m;
bool rev[MAXN];
int fa[MAXN], tr[MAXN][2];
int stack[MAXN], top = 0;
void pushdown(int x) {
    int l = tr[x][0], r = tr[x][1];
    if (rev[x]) {
        rev[x] ^= 1; rev[l] ^= 1; rev[r] ^= 1;
        swap(tr[x][0], tr[x][1]);
    }
}
inline bool isroot(int x) {
    return tr[fa[x]][0] != x && tr[fa[x]][1] != x;
}
void rotate(int x) {
    int y = fa[x], z = fa[y];
    int l, r;
    if (tr[y][0] == x) l = 0;
    else l = 1;
    r = l ^ 1;
    if (!isroot(y)) {
        if (tr[z][0] == y) tr[z][0] = x;
        else tr[z][1] = x;
    }
    fa[x] = z;
    fa[y] = x;
    fa[tr[x][r]] = y;
    tr[y][l] = tr[x][r];
    tr[x][r] = y;
}
void splay(int x) {
    top = 0;
    stack[++top] = x;
    for (int i = x; !isroot(i); i = fa[i])
        stack[++top] = fa[i];
    while (top) pushdown(stack[top--]);
    while (!isroot(x)) {
        int y = fa[x], z = fa[y];
        if (!isroot(y)) {
            if (tr[y][0] == x ^ tr[z][0] == y) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
void access(int x) {
    for (int t = 0; x; t = x, x = fa[x])
        splay(x), tr[x][1] = t;
}
void makeroot(int x) {
    access(x);
    splay(x);
    rev[x] ^= 1;
}
void link(int x, int y) {
    makeroot(x);
    fa[x] = y;
    splay(x);
}
void cut(int x, int y) {
    makeroot(x);
    access(y);
    splay(y);
    tr[y][0] = fa[tr[y][0]] = 0;
}
int find(int x) {
    access(x);
    splay(x);
    while (tr[x][0]) x = tr[x][0];
    return x;
}

void query(int x, int y) {
    if (find(x) == find(y)) printf("Yes\n");
    else printf("No\n");
}

int main() {
    scanf("%d %d", &n, &m);
    char op[10];
    int x, y;
    for (int i = 0; i < m; i++) {
        scanf("%s", op);
        scanf("%d %d", &x, &y);
        if (op[0] == 'Q') query(x, y);
        else if (op[0] == 'C') link(x, y);
        else cut(x, y);
    }
    return 0;
}
發佈了194 篇原創文章 · 獲贊 9 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章