1. 程式人生 > 其它 >Party 題解

Party 題解

一、題目:


二、思路:

這道題題面中的“資料範圍”部分的 Latex 公式 出了一些問題,向大家致以誠摯的歉意。

現在來說一下這道題怎麼搞。

我們先來考慮一個點 \(z\) 能貢獻答案的條件。題目中說了,若 \(\exists x,y\in[l,r]\) 滿足 \(\mathbb{lca}(x,y)=z\),則 \(z\) 就可以貢獻答案。那麼我們考慮能不能把這個限制在精確一點,也就是說,我們把 \(x,y\) 的編號差放到最小,每次只需要檢查 \(z\) 的子樹內編號差最小的 \(x,y\) 即可。

由此想到一種暴力演算法。

先將滿足 \(l\leq z\leq r\)\(z\) 單獨計算,然後對於每個 \(z\)

以及它的某一個兒子 \(son\),列舉 \(\mathbb{subtree}(son)\) 中的節點 \(x\in[l,r]\)。在 \(\overline {\mathbb{subtree}(son)}\) 中,找見離 \(x\) 最近的節點 \(y\)(這裡的“最近”指的是編號最近),如果 \(y\in[l,r]\),那麼 \(z\) 就可以貢獻答案。

當然這樣的複雜度是不可接受的,於是我們考慮 dsu on tree。大致地說,就是在列舉 \(x\) 時,不列舉 \(\mathbb{subtree}(\mathbb{heavyson}(z))\) 中的點,只去列舉其他子樹中的點。然後預處理出這些點在 \(z\)

處的最近點(在 \(z\) 處的意思就是:以 \(z\) 為最近公共祖先)。用啟發式合併的 set 即可。

考慮如何回答詢問。我們先將詢問離線下來,按照 \(r\) 從小到大排序。對每個點 \(z\) 維護 \(Left[z]\) 陣列,表示若 \(l\leq Left[z]\),那麼 \(z\) 就可以貢獻答案;通俗地講,\(Left[z]\) 就是 \(z\) 可以產生貢獻的最大的 \(l\)。那麼隨著 \(r\) 的遞增,\(Left[z]\) 肯定是單調不降的。

對於當前要更新的點 \(r\),令 \(z\gets r\),讓 \(z\) 順著重鏈往上跳,每次讓 \(z\) 移動到 \(\mathbb{father}(\mathbb{top}(z))\)

  1. 找見 \(r\)\(z\) 處的後繼 \(y_0\)。將點對 \((r,z)\) 掛在 \(y_0\) 處,表示若 \(r\geq y_0\),則 \(Left[z]\gets\max\{Left[z],r \}\)
  2. 找見 \(r\)\(z\) 處的前驅 \(x_0\)。直接令 \(Left[z]\gets \max\{Left[z],x_0 \}\)

這樣為什麼就能列舉到所有可能發生改變的 \(z\) 呢?考慮輕重鏈剖分的性質,一個點 \(z\) 最多隻會有一個向下墜的重邊,而上述的統計過程是在輕邊處統計的。由於 \(x\)\(y\) 一定在不同的子樹中,所以 \(z\) 要麼在 \(r\) 充當 \(x\) 的角色的時候被列舉到,要麼在 \(r\) 充當 \(y\) 的角色的時候被列舉到。

統計答案用一個權值樹狀陣列即可。

三、程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <algorithm>

using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int MAXN = 3e5 + 5, INF = 0x3f3f3f3f;

int n, Q;
int siz[MAXN], son[MAXN], fa[MAXN], top[MAXN];
int Left[MAXN], ans[MAXN];
set<int>S[MAXN];
map<int, int>lower[MAXN], upper[MAXN];
vector<int>linker[MAXN];
vector<pair<int, int> >ope[MAXN];

struct Query {
    int l, r, id;
    inline friend bool operator <(const Query &a, const Query &b) {
        return a.r < b.r;
    }
}q[MAXN];

namespace BIT {
#define lowbit(x) (x & (-x))
    int tr[MAXN];
    inline void add(int p, int x) { // 樹狀陣列的值域包含0,所以先將p++。
        ++ p;
        for (; p <= n + 1; p += lowbit(p))
            tr[p] += x;
    }
    inline int sum(int p) {
        int res = 0;
        ++ p;
        for (; p; p -= lowbit(p))
            res += tr[p];
        return res;
    }
    inline int query(int l, int r) {
        return sum(r) - sum(l - 1);
    }
}

void dfs1(int x, int father) {
    siz[x] = 1;
    fa[x] = father;
    for (auto &y : linker[x]) {
        if (y == father) continue;
        dfs1(y, x);
        if (siz[y] > siz[son[x]]) son[x] = y;
        siz[x] += siz[y];
    }
}

void dfs2(int x, int first) {
    top[x] = first;
    if (son[x]) dfs2(son[x], first);
    for (auto &y : linker[x]) {
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
    }
}

void dfs3(int x) {
    if (!son[x]) { S[x].insert(x); return; }
    dfs3(son[x]);

    for (int i = 0; i < (int)linker[x].size(); ++ i) {
        int y = linker[x][i];
        if (y == fa[x] || y == son[x]) continue;
        dfs3(y);

        for (auto &p : S[y]) {
            lower[p][x] = 0;
            upper[p][x] = INF;

            set<int>::iterator it = S[x].lower_bound(p);
            if (it != S[x].end()) upper[p][x] = *it;
            if (it != S[x].begin()) { -- it; lower[p][x] = *it; }

            it = S[son[x]].lower_bound(p);
            if (it != S[son[x]].end()) upper[p][x] = min(upper[p][x], *it);
            if (it != S[son[x]].begin()) { -- it; lower[p][x] = max(lower[p][x], *it); }
        }
        for (auto &p : S[y]) S[x].insert(p);
    }

    swap(S[x], S[son[x]]);
    for (int i = (int)linker[x].size() - 1; i >= 0; -- i) {
        int y = linker[x][i];
        if (y == fa[x] || y == son[x]) continue;

        for (auto &p : S[y]) {
            set<int>::iterator it = S[x].lower_bound(p);
            if (it != S[x].end()) upper[p][x] = min(upper[p][x], *it);
            if (it != S[x].begin()) { -- it; lower[p][x] = max(lower[p][x], *it); }
        }
        for (auto &p : S[y]) S[x].insert(p);
    }
    S[x].insert(x);
}

int main() {
    FILEIN("party.in"); FILEOUT("party.out");
    n = read(); Q = read();
    for (int i = 1; i < n; ++ i) {
        int x = read(), y = read();
        linker[x].push_back(y);
        linker[y].push_back(x);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    dfs3(1);
    for (int i = 1; i <= Q; ++ i) {
        q[i].l = read(); q[i].r = read();
        q[i].id = i;
    }
    sort(q + 1, q + Q + 1);
    int R = 0;
    BIT::add(0, n);
    for (int i = 1; i <= Q; ++ i) {
        while (R < q[i].r) {
            ++ R;
            // Left[R] = max(Left[R], R);
            if (R > Left[R]) {
                BIT::add(Left[R], -1);
                Left[R] = R;
                BIT::add(Left[R], 1);
            }
            for (auto &p : ope[R]) {
                int x = p.first, z = p.second;
                // Left[z] = max(Left[z], x);
                if (x > Left[z]) {
                    BIT::add(Left[z], -1);
                    Left[z] = x;
                    BIT::add(Left[z], 1);
                }
            }

            int x = fa[top[R]];
            while (x) {
                int y0 = upper[R][x];
                if (y0 <= n) ope[y0].push_back({ R, x });

                int x0 = lower[R][x];
                // Left[x] = max(Left[x], x0);
                if (x0 > Left[x]) {
                    BIT::add(Left[x], -1);
                    Left[x] = x0;
                    BIT::add(Left[x], 1);
                }

                x = fa[top[x]];
            }
        }
        ans[q[i].id] = BIT::query(q[i].l, n);
    }
    for (int i = 1; i <= Q; ++ i) {
        printf("%d\n", ans[i]);
    }
    return 0;
}