[POI2008]BLO-Blockade

题目传送门

题意:给定一张无向图,求每个点被封锁之后有多少个有序点对$(x,y)(x!=y,1<=x,y<=n)$满足$x$无法到达$y$。

题目强调了所有村庄都相互可达。

首先会想到割顶,因为如果删去割顶,就会导致图的连通块增加。反之不变。

不是割顶答案就应该是$(n-1)*2$。因为连通块没有增加意味着除去该点其他任意点对都可直接或间接可达,不可达的是该点与其他点之间来回的连接。

如果是割顶,实际上答案就是上面的基础上加上各个连通块之间的答案。

但如果删去割顶再判断复杂度变成$O(n^2)$会$TLE$。

于是我们在$Tarjan$的过程中顺便求下。

如果删去这个点有$n$个连通块,每个的大小为$son_i$,那么答案应该是$2\sum_{i=1}^n \sum_{j=i+1}^n son_i \times son_j$。

变形下也就是$son_i$要与所有$son_j,j=\{ 1,2,..,i-1\}$相乘。

所以我们每统计一个连通块只要维护$son_i$和$\sum_{j=1}^{i-1}son_j$即可。

在$Tarjan$过程中,就是$dfs$其相连结点时,如果子结点及其后继结点不能访问到此结点的祖先结点则说明该结点底下是连通块,应该统计。

最后会剩余该结点的祖先结点,它们会构成连通块,大小为$n-\sum_{i=1}^n son_i - 1$。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define re register
 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i)
 7 #define repd(i, a, b) for (re int i = a; i >= b; --i)
 8 #define maxx(a, b) a = max(a, b);
 9 #define minn(a, b) a = min(a, b);
10 #define LL long long
11 #define inf (1 << 30)
12 
13 inline int read() {
14     int w = 0, f = 1; char c = getchar();
15     while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar();
16     while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar();
17     return w * f;
18 }
19 
20 const int maxn = 1e5 + 10, maxm = 5e5 + 10;
21 
22 struct Edge {
23     int u, v, pre;
24 };
25 
26 struct Graph {
27     Edge edges[maxm << 1];
28     int n, m;
29     int G[maxn];
30     int dfs_clock, low[maxn], pre[maxn], iscut[maxn];
31     LL sum[maxn], sc[maxn], ans[maxn];
32     void init(int n) {
33         this->n = n;
34         m = 0;
35         memset(G, 0, sizeof(G));
36     }
37     void Add(int u, int v) {
38         edges[++m] = (Edge){u, v, G[u]};
39         G[u] = m;
40     }
41     // int tag[maxn], tot[maxn];
42     void dfs(int u, int fa) {
43         low[u] = pre[u] = ++dfs_clock;
44         int child = 0;
45         for (register int i = G[u]; i; i = edges[i].pre) {
46             int v = edges[i].v;
47             if (!pre[v]) {
48                 child++;
49                 dfs(v, u);
50                 if (low[v] >= pre[u]) {
51                     ans[u] += sum[u] * sc[v];
52                     sum[u] += sc[v];
53                 }
54                 sc[u] += sc[v];
55                 minn(low[u], low[v]);
56                 if (low[v] >= pre[u]) iscut[u] = 1;
57             } else {
58                 if (v != fa) 
59                     minn(low[u], pre[v]);
60             }
61         }
62         if (u == fa && child == 1) iscut[u] = 0;
63         sc[u]++;
64     }
65     void Tarjan() {
66         dfs_clock = 0;
67         rep(i, 1, n)
68             if (!pre[i])
69                 dfs(i, i);
70         rep(i, 1, n) 
71             if (!iscut[i]) {
72                 printf("%lld\n", (n-1LL) << 1);
73             }
74             else printf("%lld\n", (ans[i] + (n-sum[i]-1)*sum[i] + n-1) << 1);
75     }
76 } G;
77 
78 int n, m;
79 
80 int main() {
81     n = read(), m = read(); G.init(n);
82     rep(i, 1, m) {
83         int u = read(), v = read();
84         G.Add(u, v); G.Add(v, u);
85     }
86     G.Tarjan();
87     return 0;
88 }

 

posted @ 2019-02-02 14:44  AC-Evil  阅读(134)  评论(1编辑  收藏  举报