[SDOI2018]戰略遊戲 圓方樹,樹鏈剖分
阿新 • • 發佈:2019-01-20
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]戰略遊戲 圓方樹,樹鏈剖分