强连通分量
一些性质
- 有向图中,每一个点仅属于一个强连通分量。
- 强连通分量具有传递性。
- 极大的强连通分量指的是一个强连通分量无法继续扩大。
Tarjan 算法的一些细节
- 当一个节点的 DFN 和 LOW 相等时,则发现了一个强连通分量,进行弾栈。
一些例题
P2002:消息扩散题解
求割点
类似于之前的 Tarjan 算法,只要\(dfn[u]\leq low[u]\),就可以认为这个点是一个割点。理解:下游的点无法除了通过点 u 的路径到达上游 dfn 更小的点,那么这个就是割点。但是还需要考虑点\(u\)为根节点的情况,因为如果点\(u\)为根的话,那么只要子树大于二就算是一个割点,因为子树间的联通必须需要根节点作为枢纽。
模板题代码:
const int MAX_N = 20010, MAX_M = 100010;
int head[MAX_N], current, n, m, tmpx, tmpy, low[MAX_N], dfn[MAX_N], tot;
void addpath(int src, int dst)
edges[current].to = dst, edges[current].nxt = head[src];
void tarjan(int u, int fa)
for (int i = head[u]; i != -1; i = edges[i].nxt)
tarjan(edges[i].to, fa), low[u] = min(low[u], low[edges[i].to]);
if (low[edges[i].to] >= dfn[u] && u != fa)
low[u] = min(low[u], dfn[edges[i].to]);
if (child >= 2 && u == fa)
memset(head, -1, sizeof(head));
for (int i = 1; i <= m; i++)
scanf("%d%d", &tmpx, &tmpy), addpath(tmpx, tmpy), addpath(tmpy, tmpx);
for (int i = 1; i <= n; i++)
for (int i = 1; i <= n; i++)
for (int i = 1; i <= n; i++)
// P3388.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 20010, MAX_M = 100010;
int head[MAX_N], current, n, m, tmpx, tmpy, low[MAX_N], dfn[MAX_N], tot;
bool ans[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_M << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void tarjan(int u, int fa)
{
low[u] = dfn[u] = ++tot;
int child = 0;
for (int i = head[u]; i != -1; i = edges[i].nxt)
{
if (!dfn[edges[i].to])
{
tarjan(edges[i].to, fa), low[u] = min(low[u], low[edges[i].to]);
if (low[edges[i].to] >= dfn[u] && u != fa)
ans[u] = true;
if (u == fa)
child++;
}
low[u] = min(low[u], dfn[edges[i].to]);
}
if (child >= 2 && u == fa)
ans[u] = true;
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
int cnt = 0;
for (int i = 1; i <= m; i++)
scanf("%d%d", &tmpx, &tmpy), addpath(tmpx, tmpy), addpath(tmpy, tmpx);
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i, i);
for (int i = 1; i <= n; i++)
if (ans[i])
cnt++;
printf("%d\n", cnt);
for (int i = 1; i <= n; i++)
if (ans[i])
printf("%d ", i);
return 0;
}
// P3388.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 20010, MAX_M = 100010;
int head[MAX_N], current, n, m, tmpx, tmpy, low[MAX_N], dfn[MAX_N], tot;
bool ans[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_M << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void tarjan(int u, int fa)
{
low[u] = dfn[u] = ++tot;
int child = 0;
for (int i = head[u]; i != -1; i = edges[i].nxt)
{
if (!dfn[edges[i].to])
{
tarjan(edges[i].to, fa), low[u] = min(low[u], low[edges[i].to]);
if (low[edges[i].to] >= dfn[u] && u != fa)
ans[u] = true;
if (u == fa)
child++;
}
low[u] = min(low[u], dfn[edges[i].to]);
}
if (child >= 2 && u == fa)
ans[u] = true;
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
int cnt = 0;
for (int i = 1; i <= m; i++)
scanf("%d%d", &tmpx, &tmpy), addpath(tmpx, tmpy), addpath(tmpy, tmpx);
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i, i);
for (int i = 1; i <= n; i++)
if (ans[i])
cnt++;
printf("%d\n", cnt);
for (int i = 1; i <= n; i++)
if (ans[i])
printf("%d ", i);
return 0;
}
双联通分量
点双连通分量
概念:在无向图中,对于点对\(u,v\),在图上删除除\(u,v\)以外的任意一个点,仍然联通,那么称这个点对为点双联通分量。
边双连通分量
概念:在无向图中,对于点对\(u,v\),在图上任意删除一条边,\(u,v\)仍连通,则称其为边双连通分量。
二分图
https://www.renfei.org/blog/bipartite-matching.html
性质
二分图中,选取最少的点数,使这些点和所有的边都有关联(把所有的边的覆盖),叫做最小点覆盖。
二分图最小点覆盖 = 二分图最大匹配
二分图最大点独立集 = 总点数 – 二分图最大总匹配
例题:[USACO2006NOV]Asteriod,[Usaco2005 jan]Muddy Fields
匈牙利算法
模板地址:P3386 【模板】二分图匹配
for (int i = head[u]; i != -1; i = edges[i].nxt)
if ((!match[to]) || dfs(match[to], tm))
bool dfs(int u, int tm)
{
for (int i = head[u]; i != -1; i = edges[i].nxt)
{
int to = edges[i].to;
if (dfn[to] != tm)
{
dfn[to] = tm;
if ((!match[to]) || dfs(match[to], tm))
{
match[to] = u;
return true;
}
}
}
return false;
}
bool dfs(int u, int tm)
{
for (int i = head[u]; i != -1; i = edges[i].nxt)
{
int to = edges[i].to;
if (dfn[to] != tm)
{
dfn[to] = tm;
if ((!match[to]) || dfs(match[to], tm))
{
match[to] = u;
return true;
}
}
}
return false;
}
我个人认为这个是相当精妙的一个写法,可以将“继续匹配”和“向上协商”完美的结合在一起,非常的优秀。
2-SAT
此模型可以用于求出多个条件约束下变量的解集。可以通过某些矛盾关系推出可行关系做有向边,Tarjan 染色即可。
例题:HDU3062:Party 题解