1. 程式人生 > >[IOI2018] werewolf 狼人 kruskal重構樹,主席樹

[IOI2018] werewolf 狼人 kruskal重構樹,主席樹

printf 們的 eof html namespace modify 二叉樹 發現 mat

[IOI2018] werewolf 狼人

LG傳送門

kruskal重構樹好題。

日常安利博客文章

這題需要搞兩棵重構樹出來,這兩棵重構樹和我們平時見過的重構樹有點不同(據說叫做點權重構樹?),根據經過我們簡化的建樹方法,這兩棵樹不再是二叉樹,但是仍具有kruskal重構樹的優秀性質,建議結合後面的描述理解。

看這題需要首先我們從\(S\)走到\(T\)轉化為分別從\(S\)\(T\)出發尋找能共同到達的點,需要快速求出從某個點出發經過點權不大(小)於\(r\)\(l\))的點,考慮kruskal重構樹。令每條邊的的邊權為所連接兩點的較大(小)值,造兩棵重構樹,這樣就可以像平時一樣直接倍增做了,但是我們發現邊權的信息實際上就是點權的信息,於是我們在建新樹時就不另建新點了,這就是所謂的“點權重構樹”。建出樹處理倍增之後,我們的問題就變成了查詢兩棵樹上兩棵子樹是否有交,用dfs序表達就是一個簡單的二維數點的問題,直接主席樹。

#include <cstdio>
#include <cctype>
#include <vector>
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 200003;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++; }
I int rd() {
    R int f = 0;
    R char c = gc();
    while (c < 48 || c > 57) c = gc();
    while (c > 47 && c < 58) f = f * 10 + (c ^ 48), c = gc();
    return f;
}
int s[N], rt[N], val[N], T;
vector <int> g[N];
struct edge { int g, s; };
struct segtree { int p, q, s; }e[N << 5];
struct kruskal {
    int h[N], f[N], fa[N][20], dfn[N], low[N], E, tim;
    edge e[N];
    I void add(int x, int y) { e[++E] = (edge){y, h[x]}, h[x] = E; }
    I int find(int x) {
        R int r = x, y;
        while (f[r] ^ r)
            r = f[r];
        while (x ^ r)
            y = f[x], f[x] = r, x = y;
        return r;
    }
    void dfs(int x) {
        dfn[x] = ++tim;
        R int i;
        for (i = 1; i < 20; ++i)
            fa[x][i] = fa[fa[x][i - 1]][i - 1];
        for (i = h[x]; i; i = e[i].s)
            dfs(e[i].g);
        low[x] = tim;
    }
}X, Y;
int modify(int k, int l, int r, int x) {
    R int t = ++T;
    e[t].p = e[k].p, e[t].q = e[k].q, e[t].s = e[k].s + 1;
    if (l == r)
        return t;
    R int m = l + r >> 1;
    if (x <= m)
        e[t].p = modify(e[k].p, l, m, x);
    else
        e[t].q = modify(e[k].q, m + 1, r, x);
    return t;
}
int query(int k, int t, int l, int r, int x, int y) {
    if (x <= l && r <= y)
        return e[t].s - e[k].s;
    R int m = l + r >> 1, o = 0;
    if (x <= m)
        o += query(e[k].p, e[t].p, l, m, x, y);
    if (m < y)
        o += query(e[k].q, e[t].q, m + 1, r, x, y);
    return o;
}
int main() {
    R int n = rd(), m = rd(), Q = rd(), i, x, y, l, r;
    for (i = 1; i <= m; ++i)
        x = rd() + 1, y = rd() + 1, g[x].push_back(y), g[y].push_back(x);
    for (i = 1; i <= n; ++i)
        X.f[i] = i, Y.f[i] = i, s[i] = g[i].size();
    for (x = n; x; --x)
        for (i = 0; i < s[x]; ++i)
            if (g[x][i] > x && (y = X.find(g[x][i])) ^ x)
                X.add(x, y), X.f[y] = X.fa[y][0] = x;
    for (x = 1; x <= n; ++x)
        for (i = 0; i < s[x]; ++i)
            if (g[x][i] < x && (y = Y.find(g[x][i])) ^ x)
                Y.add(x, y), Y.f[y] = Y.fa[y][0] = x;
    X.dfs(1), Y.dfs(n);
    for (i = 1; i <= n; ++i)
        val[X.dfn[i]] = Y.dfn[i];
    for (i = 1; i <= n; ++i)
        rt[i] = modify(rt[i - 1], 1, n, val[i]);
    while (Q--) {
        x = rd() + 1, y = rd() + 1, l = rd() + 1, r = rd() + 1;
        for (i = 19; ~i; --i)
            if (X.fa[x][i] >= l)
                x = X.fa[x][i];
        for (i = 19; ~i; --i)
            if (Y.fa[y][i] && Y.fa[y][i] <= r)
                y = Y.fa[y][i];
        printf(query(rt[X.dfn[x] - 1], rt[X.low[x]], 1, n, Y.dfn[y], Y.low[y]) ? "1\n" : "0\n");
    }
    return 0;
}

[IOI2018] werewolf 狼人 kruskal重構樹,主席樹