JZOJ3225ios
給出一張\(N\)個點\(M\)條邊的無向圖,和\(Q\)對點對\(p_i, q_i\),問最後圖中每一個點一定被\(p_i\)到\(q_i\)的路徑覆蓋多少次ui
\(N \le 1e5, M, Q \le 2e5\)spa
樣例輸入 | 樣例輸出 |
---|---|
4 4 2 1 2 1 3 2 3 1 4 4 2 4 3 |
2 1 1 2 |
考慮\(p\)到\(q\)的路徑一定通過哪些點,顯然是\(p\)到\(q\)的簡單路徑通過的割點.net
回憶圓方樹的構造,樹上度數大於\(1\)的圓點就是原圖上的割點code
那麼問題就簡單了,建出圓方樹,而後\(p\)到\(q\)路徑上的圓點答案都會加\(1\)(由於題目提及點和終點也算,就不用特殊處理了)get
我拿了個樹剖來維護,也能夠樹上差分string
#include <cstdio> #include <iostream> #include <cstring> #include <vector> #define MAXN 100005 typedef long long LL; struct Graph { struct Edge { int v, next; Edge(int _v = 0, int _n = 0):v(_v), next(_n) {} } edge[MAXN << 2]; int head[MAXN << 1], cnt; void init() { memset(head, -1, sizeof head); cnt = 0; } void add_edge(int u, int v) { edge[cnt] = Edge(v, head[u]); head[u] = cnt++; } void insert(int u, int v) { add_edge(u, v); add_edge(v, u); } }; struct SegmentTree { int add[MAXN << 3]; void update(int, int, int, int, int); int query(int, int, int, int); }; char gc(); int read(); void Tarjan(int, int); void rebuild(); void dfs1(int); void dfs2(int); int N, M, Q, idx, tot; int dfn[MAXN << 1], top[MAXN << 1], fa[MAXN << 1], dep[MAXN << 1], low[MAXN], size[MAXN << 1], heavy[MAXN << 1]; int stk[MAXN], stop; std::vector<int> bel[MAXN]; Graph G; SegmentTree sgt; int main() { G.init(); tot = N = read(), M = read(), Q = read(); for (int i = 1; i <= M; ++i) G.insert(read(), read()); Tarjan(1, 0); rebuild(); dfs1(1); top[1] = 1, idx = 0; dfs2(1); while (Q--) { int p = read(), q = read(); while (top[p] ^ top[q]) { if (dep[top[p]] < dep[top[q]]) std::swap(p, q); sgt.update(1, 1, tot, dfn[top[p]], dfn[p]); p = fa[top[p]]; } if (dep[p] > dep[q]) std::swap(p, q); sgt.update(1, 1, tot, dfn[p], dfn[q]); } for (int i = 1; i <= N; ++i) printf("%d\n", sgt.query(1, 1, tot, dfn[i])); return 0; } inline char gc() { static char buf[1000000], *p1, *p2; if (p1 == p2) p1 = (p2 = buf) + fread(buf, 1, 1000000, stdin); return p1 == p2 ? EOF : *p2++; } inline int read() { int res = 0; char ch = gc(); while (ch < '0' || ch > '9') ch = gc(); while (ch >= '0' && ch <= '9') res = (res << 1) + (res << 3) + ch - '0', ch = gc(); return res; } void SegmentTree::update(int rt, int L, int R, int l, int r) { if (L >= l && R <= r) ++add[rt]; else { int mid = (L + R) >> 1; if (l <= mid) update(rt << 1, L, mid, l, r); if (r > mid) update(rt << 1 | 1, mid + 1, R, l, r); } } int SegmentTree::query(int rt, int L, int R, int pos) { if (L == R) return add[rt]; int mid = (L + R) >> 1; if (pos <= mid) return add[rt] + query(rt << 1, L, mid, pos); else return add[rt] + query(rt << 1 | 1, mid + 1, R, pos); } void Tarjan(int u, int fa) { dfn[u] = low[u] = ++idx; for (int i = G.head[u]; ~i; i = G.edge[i].next) { int v = G.edge[i].v; if (v == fa) continue; if (!dfn[v]) { stk[stop++] = v; Tarjan(v, u); low[u] = std::min(low[u], low[v]); if (low[v] >= dfn[u]) { int p; ++tot; do { p = stk[--stop]; bel[p].push_back(tot); } while (p ^ v); bel[u].push_back(tot); } } else low[u] = std::min(low[u], dfn[v]); } } void rebuild() { G.init(); for (int i = 1; i <= N; ++i) for (int j = 0; j < bel[i].size(); ++j) G.insert(i, bel[i][j]); } void dfs1(int u) { dep[u] = dep[fa[u]] + 1; size[u] = 1; for (int i = G.head[u]; ~i; i = G.edge[i].next) { int v = G.edge[i].v; if (v == fa[u]) continue; fa[v] = u, dfs1(v); size[u] += size[v]; if (!heavy[u] || size[heavy[u]] < size[v]) heavy[u] = v; } } void dfs2(int u) { dfn[u] = ++idx; if (heavy[u]) { top[heavy[u]] = top[u]; dfs2(heavy[u]); } for (int i = G.head[u]; ~i; i = G.edge[i].next) { int v = G.edge[i].v; if (v == fa[u] || v == heavy[u]) continue; top[v] = v, dfs2(v); } } //Rhein_E