1. 程式人生 > >[SDOI2018]戰略遊戲 圓方樹,樹鏈剖分

[SDOI2018]戰略遊戲 圓方樹,樹鏈剖分

dfs序 str eof 兩個 %d sdoi n) ons html

[SDOI2018]戰略遊戲

這題是道路相遇(題解)的升級版,詢問的兩個點變成了\(S\)個點。

LG傳送門

還是先建出圓方樹,考慮對於詢問的\(S\)個點,答案就是圓方樹上能包含這些點的最小連通塊中的圓點個數減去\(S\)。問題變成了怎樣求這樣的連通塊中的圓點個數,直接給結論吧:先搞出樹的dfs序,把詢問的點按dfs序從小到大排一遍序,每次把答案加上第\(i\)和第\(i + 1\)個點之間的圓點個數,但是不算lca,再加上第\(1\)個和第\(S\)個點之間的圓點個數,然後除以二就得到了這個連通塊內不包括整個連通塊的lca的圓點個數,可以證明這個連通塊內除了lca的所有點都被算了兩次,最後判斷一下lca是不是圓點,減去\(S\)

就是答案。

實測樹剖比倍增快很多。

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define R register
#define I inline
#define B 10000000
using namespace std;
const int N = 400003;
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 h[N], H[N], sta[N], dfn[N], low[N], vis[N], fa[N], dep[N], siz[N], son[N], top[N], dis[N], q[N], n, c, tim, cnt, stp;
struct edge { int s, g; }e[N], E[N];
I void add(int x, int y) { e[++c] = (edge){h[x], y}, h[x] = c; }
I void Add(int x, int y) { E[++c] = (edge){H[x], y}, H[x] = c; }
I int min(int x, int y) { return x < y ? x : y; }
I int cmp(int x, int y) { return dfn[x] < dfn[y]; }
void dfs(int x) {
    vis[sta[++stp] = x] = 1, dfn[x] = low[x] = ++tim;
    for (R int i = h[x], y, z; i; i = e[i].s)
        if (!dfn[y = e[i].g]) {
            dfs(y), low[x] = min(low[x], low[y]);
            if (low[y] >= dfn[x]) {
                Add(++cnt, x), Add(x, cnt);
                do {
                    vis[z = sta[stp--]] = 0, Add(cnt, z), Add(z, cnt);
                } while (z ^ y);
            }
        }
        else
            low[x] = min(low[x], dfn[y]);
}
void dfs1(int x, int f) {
    fa[x] = f, dep[x] = dep[f] + 1, siz[x] = 1, dis[x] = dis[f] + (x <= n);
    for (R int i = H[x], y, m = 0; i; i = E[i].s)
        if ((y = E[i].g) ^ f) {
            dfs1(y, x), siz[x] += siz[y];
            if (siz[y] > m)
                m = siz[x], son[x] = y;
        }
}
void dfs2(int x, int r) {
    dfn[x] = ++tim, top[x] = r;
    if (son[x])
        dfs2(son[x], r);
    for (R int i = H[x], y; i; i = E[i].s)
        if ((y = E[i].g) ^ fa[x] && y ^ son[x])
            dfs2(y, y);
}
I int lca(int x, int y) {
    while (top[x] ^ top[y])
        dep[top[x]] > dep[top[y]] ? x = fa[top[x]] : y = fa[top[y]];
    return dep[x] < dep[y] ? x : y;
}
I int query(int x, int y) { return dis[x] + dis[y] - (dis[lca(x, y)] << 1); }
int main() {
    R int T = rd(), m, Q, S, i, x, y, ans;
    while (T--) {
        memset(h, 0, sizeof h), memset(H, 0, sizeof H), memset(son, 0, sizeof son), memset(dfn, 0, sizeof dfn);
        cnt = n = rd(), m = rd(), c = 0;
        for (i = 1; i <= m; ++i)
            x = rd(), y = rd(), add(x, y), add(y, x);
        c = tim = stp = 0, dfs(1), tim = 0, dfs1(1, 0), dfs2(1, 1), Q = rd();
        while (Q--) {
            S = rd();
            for (i = 1; i <= S; ++i)
                q[i] = rd();
            sort(q + 1, q + S + 1, cmp), ans = query(q[1], q[S]);
            for (i = 2; i <= S; ++i)
                ans += query(q[i - 1], q[i]);
            printf("%d\n", (ans >> 1) - S + (lca(q[1], q[S]) <= n));
        }
    }
    return 0;
}

[SDOI2018]戰略遊戲 圓方樹,樹鏈剖分