图论
连通性相关
SCC & 缩点
这个概念是在有向图里的。
void tarj(ll u) {
dfn[u] = low[u] = ++ tim;
stk.push(u); instk[u] = true;
for (auto x : G[u]) {
if (!dfn[x]) {
tarj(x);
low[u] = min(low[u], low[x]);
}
else if (instk[x])
low[u] = min(low[u], dfn[x]);
}
if (low[u] == dfn[u]) {
++ tot; ll x;
do {
x = stk.top(); stk.pop();
scc[tot].pb(x);
bel[x] = tot;
instk[x] = false;
} while (x != u);
}
}
缩点就是把相同 scc 的点缩成一个,然后在新的 dag 上做事情。
割点 / 点双
无向图里定义。
割点:如果去掉某个点与其出边,这个图就不再连通,这个点就是割点。
点双:不存在割点的图即为点双连通图,一个图中的每个极大点双连通子图称即为一个点双。
求割点
void tarj(ll u, ll rt) {
dfn[u] = low[u] = ++ tim;
ll son = 0;
for (auto x : G[u]) {
if (!dfn[x]) {
tarj(x, rt);
low[u] = min(low[u], low[x]);
++ son;
if (u != rt && low[x] >= dfn[u]) vis[u] = true;
}
else
low[u] = min(low[u], dfn[x]);
}
if (u == rt && son > 1)
vis[u] = true;
}
求点双
void tarj(ll u, ll rt) {
dfn[u] = low[u] = ++ tim; stk.push(u);
if (u == rt && !G[u].size()) {
bcc[++ nbcc].pb(u);
return ;
}
for (auto v : G[u]) {
if (!dfn[v]) {
tarj(v, rt);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
++ nbcc; ll x;
do {
x = stk.top(); stk.pop();
bcc[nbcc].pb(x);
} while (x != v);
bcc[nbcc].pb(u);
}
}
else
low[u] = min(low[u], dfn[v]);
}
}
杂题
P4819
显然最坏情况下,我们需要得知每个人的身份。考虑我们第一次问的一个人,如果他汇报出了若干个平民,我们肯定会继续问这些平民直到汇报出杀手。考虑缩点,相同 scc 内的人看作一个。那么在这个 dag 上,对于那些入度为 \(0\) 的点我们显然都要问一次,记个数为 \(c\),答案不就是 \(1 - \frac{c}{n}\)。
但是考虑排除法。如果存在某个 scc,它的大小为 \(1\) 且出边到达的点不依赖它(入度不为 \(1\)),这个 scc 内的点可以不问,用剩下 \(n-1\) 个点来反推出他即可。如果存在就 \(c \to c-1\) 即可。
P3225
那我菜了呗。
显然考虑求割点。把割点删掉之后图断成了若干连通块,对每个连通块分别考虑:
- 连通块中没割点。说明该连通块孤立,需要建两个,随便选两个建即可。
- 连通块中有 \(1\) 个割点。把割点删掉就不合法了,需要建 \(1\) 个。这是因为如果建的位置塌了,可以跑出去。
- 连通块有 \(2\) 个割点。无需建立。可以任选一边跑掉,总能跑到情况 \(2\) 的连通块。
那么做完了。考虑怎么实现,每次选了一个不是割点的点开搜,遇到割点就统计,不访问即可。
P1407
很妙啊。给一对夫妻连边,男到女连边。给情人女到男连边。然后可以发现,换配偶的过程等价于走一个环。判断一对夫妻是否在同一 scc 即可。
浙公网安备 33010602011771号