BLO-Blockade[POI2008]
阿新 • • 發佈:2020-07-31
題目描述
在Byteotia有 \(n\) 個城鎮。 一些城鎮之間由無向邊連線。在城鎮外沒有十字路口,儘管可能有橋,隧道或者高架公路(反正不考慮這些)。每兩個城鎮之間至多隻有一條直接連線的道路。人們可以從任意一個城鎮直接或間接到達另一個城鎮。每個城鎮都有一個公民,他們被孤獨所困擾。事實證明,每個公民都想拜訪其他所有公民一次(在主人所在的城鎮)。所以,一共會有 \(n*(n-1)\) 次拜訪。
不幸的是,一個程式設計師總罷工正在進行中,那些程式設計師迫切要求購買某個軟體。作為抗議行動,程式設計師們計劃封鎖一些城鎮,阻止人們進入,離開或者路過那裡。正如我們所說,他們正在討論選擇哪些城鎮會導致最嚴重的後果。
編寫一個程式:
讀入Byteotia的道路系統,對於每個被決定的城鎮,如果它被封鎖,有多少訪問不會發生,輸出結果。
輸入格式
第一行讀入 \(n,m (1\le n\le 100\ 000, 1\le m\le 500\ 000)\),分別是城鎮數目和道路數目
城鎮編號 \(1\sim n\)
接下來 \(m\) 行每行兩個數字 \(a,b (1\le a<b\le n)\),表示 \(a\) 和 \(b\) 之間有一條無向邊
輸出格式
輸出 \(n\) 行,每行一個數字,為第 \(i\) 個城鎮被鎖時不能發生的訪問的數量。
題解
顯然,只有被封鎖的點為割點時,才會出現無法訪問的情況
所以求出所有點雙連通分量,對原圖建出圓方樹,只需要找出對於每個非葉子的圓點(原圖中的割點),有多少條圓點到圓點的路徑經過它。當這個割點被封鎖後,這些路徑就無法訪問了
把圓點的權值看作 \(1\),方點權值看作 \(0\),然後維護子樹權值和就可以求出答案
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; template<typename T> inline void read(T &num) { T x = 0, f = 1; char ch = getchar(); for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1; for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0'); num = x * f; } int n, nn, m; int head[100005], pre[1000005], to[1000005], sz; int siz[200005]; ll ans[100005]; void addedge(int u, int v) { pre[++sz] = head[u]; head[u] = sz; to[sz] = v; pre[++sz] = head[v]; head[v] = sz; to[sz] = u; } int dfn[100005], low[100005], tme, q[100005], top; bool gd[100005]; vector<int> e[200005]; void tarjan(int x) { dfn[x] = low[x] = ++tme; q[++top] = x; int now = 0; for (int i = head[x]; i; i = pre[i]) { int y = to[i]; if (!dfn[y]) { tarjan(y); low[x] = min(low[x], low[y]); if (dfn[x] <= low[y]) { n++; int z = 0; do { z = q[top]; e[n].push_back(z); e[z].push_back(n); top--; } while (z != y); e[n].push_back(x); e[x].push_back(n); } } else { low[x] = min(low[x], dfn[y]); } } } void dfs(int x, int fa) { if (x <= nn) siz[x] = 1; for (auto y : e[x]) { if (y == fa) continue; dfs(y, x); if (x <= nn) { ans[x] += 1ll * siz[x] * siz[y]; } siz[x] += siz[y]; } if (x <= nn) { ans[x] += 1ll * siz[x] * (nn - siz[x]); } } int main() { read(n); nn = n; read(m); for (int i = 1; i <= m; i++) { int a, b; read(a); read(b); addedge(a, b); } tarjan(1); dfs(1, 0); for (int i = 1; i <= nn; i++) { printf("%lld\n", ans[i] * 2); //u->v和v->u都要算 } return 0; }