bzoj2733: [HNOI2012]永无乡 线段树合并&Splay启发式合并

bzoj2733: [HNOI2012]永无乡

Description

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。

Input

输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000
对于 100%的数据 n≤100000,m≤n,q≤300000

Output

对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表 示所询问岛屿的编号。如果该岛屿不存在,则输出-1。

Sample Input

5 1
4 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3

Sample Output

-1
2
5
1
2

分析

这是一道启发式合并的经典题。
一般地有Splay启发式合并和线段树合并两种方法。
线段树合并就建立权值线段树然后二分查找,暴力合并。
参照线段树合并
Splay启发式合并就是把小的合并到大的里面(中序遍历合并)。
参照Splay启发式合并

代码

线段树:

/**************************************************************
    Problem: 2733
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:1832 ms
    Memory:26292 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
const int T = 2e6 + 10, N = 1e5 + 10;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
char readchar() {
    char ch = getchar();
    while(ch != 'Q' && ch != 'B') ch = getchar();
    return ch;
}
int s[T], ls[T], rs[T], f[N], a[N], rt[N], id[N], sz, n, m;
void Ins(int &p, int L, int R, int v) {
    ++s[p ? p : p = ++sz]; if(L == R) return;
    int mid = L + R >> 1;
    if(v <= mid) Ins(ls[p], L, mid, v); 
    else Ins(rs[p], mid + 1, R, v);
}
int Query(int p, int L, int R, int k) {
    if(L == R) return L;
    int mid = L + R >> 1;
    if(s[ls[p]] >= k) return Query(ls[p], L, mid, k);
    else return Query(rs[p], mid + 1, R, k - s[ls[p]]);
}
int Merge(int u, int v) {
    if(!u || !v) return u + v;
    ls[u] = Merge(ls[u], ls[v]);
    rs[u] = Merge(rs[u], rs[v]);
    s[u] = s[ls[u]] + s[rs[u]];
    return u;
}
int F(int u) {return !f[u] ? u : f[u] = F(f[u]);}
int main() {
    n = read(); m = read();
    for(int i = 1;i <= n; ++i) a[i] = read();
    while(m--) {
        int x = F(read()), y = F(read());
        if(x != y) f[x] = y;
    }
    for(int i = 1;i <= n; ++i) Ins(rt[F(i)], 1, n, a[i]), id[a[i]] = i;
    int t = read();
    while(t--) {
        char opt = readchar(); int x = F(read()), y = read();
        if(opt == 'Q')
            if(s[rt[x]] < y) puts("-1");
            else printf("%d\n", id[Query(rt[x], 1, n, y)]);
        else {
            y = F(y);
            if(x != y) f[x] = y, rt[y] = Merge(rt[x], rt[y]);
        }
    }
    return 0;
}


Splay合并

/**************************************************************
    Problem: 2733
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:3144 ms
    Memory:12620 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5 + 10, T = 5e5 + 20;
int read() {
    char ch = getchar(); int x = 0;
    for(;ch < '0' || ch > '9'; ch = getchar()) ;
    for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
    return x;
}
int fa[T], ch[T][2], siz[T], v[T], be[N], a[N], g[N], rt[N], n, m, sz;
void Link(int f, int c, int p) {fa[f > n ? ch[f][c] = p : p] = f; }
int wh(int p) {return ch[fa[p]][1] == p;}
void Update(int p) {siz[p] = siz[ch[p][0]] + siz[ch[p][1]] + 1;}
int Ne(int val) {++sz; siz[sz] = 1; v[sz] = val; return sz;}
int F(int x) {return !g[x] ? x : g[x] = F(g[x]);}
void Rotate(int p) {
    int f = fa[p], g = fa[f], c = wh(p);
    Link(g, wh(f), p); Link(f, c, ch[p][c ^ 1]);
    Link(p, c ^ 1, f); Update(f);
}
void Splay(int p, int tar) {
    for(;fa[p] != tar; Rotate(p))
        if(fa[fa[p]] != tar) Rotate(wh(p) == wh(fa[p]) ? fa[p] : p);
    Update(p); if(tar <= n) rt[tar] = p;
}
void Ins(int top, int val) {
    int p = rt[top], f;
    for(;p; p = ch[p][val > v[p]]) f = p;
    Link(f, val > v[f], p = Ne(val));
    Splay(p, top);
}
int Kth(int top, int k) {
    int p = rt[top]; if(k > siz[p]) return -1;
    for(;siz[ch[p][0]] + 1 != k;)
        k <= siz[ch[p][0]] ? p = ch[p][0] : (k -= siz[ch[p][0]] + 1, p = ch[p][1]);
    Splay(p, top); return v[p];
}
void Dfs(int p, int rt) {if(!p) return; Dfs(ch[p][0], rt); Ins(rt, v[p]); Dfs(ch[p][1], rt);}
void Merge(int u, int v) {
    if(u == v) return;
    if(siz[rt[u]] < siz[rt[v]]) swap(u, v);
    g[v] = u; Dfs(rt[v], u); 
}
int main() {
    n = read(); m = read(); sz = n;
    for(int i = 1;i <= n; ++i) a[i] = read(), be[a[i]] = i;
    for(int i = 1;i <= n; ++i)
        fa[rt[i] = Ne(a[i])] = i;
    while(m--) Merge(F(read()), F(read())); char op;
    for(int m = read(), ans, x, y; m--;) {
        for(op = getchar(); op != 'B' && op != 'Q'; op = getchar()) ; x = read(); y = read();
        if(op == 'B') Merge(F(x), F(y));
        else printf("%d\n", ~(ans = Kth(F(x), y)) ? be[ans] : ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/79559337