连通分量
通过 Tarjan对有向图缩点
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7, inf = 1e9 + 7;
struct edge {
int u, v;
} e[N];
int dfn[N], lt[N], st[N], low[N], a[N], siz[N], f[N], indu[N];
int n, m, tim, ans = -inf;
queue <int> q;
vector <int> g[N], G[N];
inline void Tarjan(int u)
{
low[u] = dfn[u] = ++ tim;
st[++ st[0]] = u;
for (int v : g[u])
{
if (!dfn[v])
{
Tarjan(v);
low[u] = min(low[v], low[u]);
}
else if (!lt[v])
{
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u])
{
lt[u] = ++ lt[0];
siz[lt[0]] += a[u];
while (st[st[0]] != u)
{
lt[st[st[0]]] = lt[0];
siz[lt[0]] += a[st[st[0]]];
st[0] --;
}
st[0] --;
}
}
int main()
{
scanf("%lld%lld",&n,&m);
for (int i = 1; i <= n; ++ i)
{
scanf("%lld",&a[i]);
}
for (int i = 1; i <= m; ++ i)
{
scanf("%lld%lld",&e[i].u,&e[i].v);
g[e[i].u].push_back(e[i].v);
}
for (int i = 1; i <= n; ++ i)
{
if (!dfn[i]) Tarjan(i);
}
for (int i = 1; i <= m; ++ i)
{
int u = lt[e[i].u];
int v = lt[e[i].v];
if (u == v) continue;
G[u].push_back(v);
indu[v] ++;
}
for (int i = 1; i <= lt[0]; ++ i)
{
// f[i] = -inf;
if (indu[i] == 0)
{
q.push(i);
f[i] = siz[i];
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
for (int v : G[u])
{
f[v] = max(f[v], f[u]+siz[v]);
indu[v] --;
if(indu[v] == 0) q.push(v);
}
}
for (int i = 1; i <= lt[0]; ++ i) ans = max(ans, f[i]);
printf("%lld",ans);
return 0;
}
通过 Tarjan 所求出的割点
#include <bits/stdc++.h>
using namespace std;
int low[21000], dfn[21000], n, m, tim, root, cnt;
vector <int> g[21000];
bool vis[21000];
inline void Tarjan(int u, int fa)
{
dfn[u] = low[u] = ++ tim;
int col = 0;
for (int v : g[u])
{
if (!dfn[v])
{
col ++;
Tarjan(v, u);
low[u] = min(low[u], low[v]);
// 两种割点的判断方式
if (u == root && col > 1)
{
if (!vis[u]) vis[u] = 1, cnt ++;
}
if (u != root && dfn[u] <= low[v])
{
if (!vis[u]) vis[u] = 1, cnt ++;
}
}
else if (fa != v)
{
low[u] = min(low[u], dfn[v]);
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1, u, v; i <= m; ++ i)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; ++ i)
{
if (!dfn[i])
{
root = i;
Tarjan(i, 0);
}
}
cout << cnt << '\n'; // 割点数量
for (int i = 1; i <= n; ++ i)
{
if (vis[i]) cout << i << ' '; // 割点
}
return 0;
}
通过 Tarjan 所求出的桥
#include <bits/stdc++.h>
using namespace std;
const int KI = 5e6 + 7;
struct edge
{
int v, nxt; bool flag;
} e[KI];
int dfn[KI], low[KI], lt[KI], hd[KI];
int tim, dcc, n, m, cnt = 1;
vector <int> bridge[KI];
inline void addedge(int u, int v)
{
e[++ cnt].v = v; e[cnt].nxt = hd[u]; hd[u] = cnt;
}
inline void tarjan(int u, int edge_in)
{
dfn[u] = low[u] = ++ tim;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].v;
if (!dfn[v])
{
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (dfn[u] < low[v])
{
e[i].flag = e[i^1].flag = 1; // 该边为桥
}
}
else if (i != (edge_in^1))
{
low[u] = min(low[u], dfn[v]);
}
}
}
inline void dfs(int u)
{
lt[u] = dcc;
bridge[dcc].push_back(u);
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].v;
if (lt[v] || e[i].flag) continue;
dfs(v);
}
}
int main()
{
cin >> n >> m;
for (int i = 1, u, v; i <= m; ++ i)
{
cin >> u >> v;
if (u == v) continue;
addedge(u, v);
addedge(v, u);
}
for (int i = 1; i <= n; ++ i)
{
if (!dfn[i]) tarjan(i, 0);
}
return 0;
}
边双连通分量(e-DCC,Tarjan)
#include <bits/stdc++.h>
using namespace std;
const int KI = 5e6 + 7;
struct edge
{
int v, nxt; bool flag;
} e[KI];
int dfn[KI], low[KI], lt[KI], hd[KI];
int tim, dcc, n, m, cnt = 1;
vector <int> bridge[KI];
inline void addedge(int u, int v)
{
e[++ cnt].v = v; e[cnt].nxt = hd[u]; hd[u] = cnt;
}
inline void tarjan(int u, int edge_in)
{
dfn[u] = low[u] = ++ tim;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].v;
if (!dfn[v])
{
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (dfn[u] < low[v])
{
e[i].flag = e[i^1].flag = 1;
}
}
else if (i != (edge_in^1))
{
low[u] = min(low[u], dfn[v]);
}
}
}
inline void dfs(int u)
{
lt[u] = dcc;
bridge[dcc].push_back(u);
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].v;
if (lt[v] || e[i].flag) continue;
dfs(v);
}
}
int main()
{
cin >> n >> m;
for (int i = 1, u, v; i <= m; ++ i)
{
cin >> u >> v;
if (u == v) continue;
addedge(u, v);
addedge(v, u);
}
for (int i = 1; i <= n; ++ i)
{
if (!dfn[i]) tarjan(i, 0);
}
for (int i = 1; i <= n; ++ i)
{
if (!lt[i])
{
dcc ++; dfs(i);
}
}
cout << dcc << '\n';
for (int i = 1; i <= dcc; ++ i)
{
cout << bridge[i].size() << ' ';
for (int u : bridge[i])
{
cout << u << ' ';
}
cout << '\n';
}
return 0;
}
点双连通分量(v-DCC,Tarjan)
#include <bits/stdc++.h>
using namespace std;
int low[5100000], dfn[5100000], st[5100000];
int n, m, tim, root, dcc, top;
vector<int> g[5100000], nod[5100000];
void dfs(int u, int fa)
{
dfn[u] = low[u] = ++tim;
int col = 0;
st[++top] = u;
for (int v : g[u])
{
if (!dfn[v])
{
col++;
dfs(v, u);
low[u] = min(low[v], low[u]);
if (dfn[u] <= low[v])
{
dcc++;
while (st[top + 1] != v)
{
nod[dcc].push_back(st[top--]);
}
nod[dcc].push_back(u);
}
}
else if (fa != v)
{
low[u] = min(low[u], dfn[v]);
}
}
if (u == root && col == 0)
{
nod[++dcc].push_back(u);
}
}
int main()
{
cin >> n >> m;
for (int i = 1, u, v; i <= m; ++i)
{
cin >> u >> v;
if (u == v) continue;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; ++i)
{
if (!dfn[i])
{
dfs(root = i, 0);
}
}
cout << dcc << '\n';
for (int i = 1; i <= dcc; ++i)
{
cout << nod[i].size() << ' ';
for (int u : nod[i]) cout << u << ' ';
cout << '\n';
}
return 0;
}
最小生成树
Kruskal
void Kruskal() {
for (int i = 1; i <= n; ++ i) fa[i] = i; //并查集 预处理
sort(e + 1, e + 1 + m, cmp); //按照 边权大小 进行排序
for (int i = 1; i <= m; ++ i) {
int fat_u = fat(e[i].u);
int fat_v = fat(e[i].v);
if (fat_u == fat_v) continue; //判断是否 已经连边
fa[fat_u] = fat_v; //加边操作
ans += e[i].w; //记录边权和
//………………
tot ++ ;
if (tot == n - 1) break; //树的边数为点数-1 因此跳出
}
}
Prim
void Prim() {
for (int i = 1; i <= n; ++ i) {
int k = 0
for (int j = 1; j <= n; ++ j) //找一个与白点相连的权值最小的蓝点k
if(!vis[j] && mp[j] < mp[k])
k = j;
vis[k] = true; //蓝点k加入生成树,标记为白点
for (int j = 1; j <= n; ++ j)
if(!vis[j] && sm[k][j] < dis[j])
dis[j] = sm[k][j];
}
for (int i = 1; i <= n; ++ i) //累加权值
ans += dis[i];
}
Prim & heap
void Prim() {
priority_queue <PAIR> q;
int ans = 0;
dis[1] = 0;
q.push(make_pair(dis[1],1));
while (!q.empty()){
int u = q.top().second, d = q.top().first;
q.pop();
if (vis[u]) continue;
vis[u] = true;
ans += d;
for (int i = h[u]; i; i = e[i].nxt) {
int v = sm[i].v;
if (dis[v] > sm[i].w) {
dis[v] = sm[i].w;
q.push(make_pair(dis[v],v));
}
}
}
}
数据结构
ODT
struct node {
int l, r, val;
node(int il, int ir=0, int iv=0) : l(il), r(ir), val(iv) {}
bool operator < (const node &a) const {
return l < a.l;
}
};
set<node> odt;
auto split(int x)
{
if (x > n) return odt.end();
auto it = prev(odt.upper_bound(node(x)));
if (it->l == x) return it;
auto [l, r, val] = *it;
odt.erase(it); odt.insert(node(l, x - 1, val));
return odt.insert(node(x, r, val)).first;
}
void assign(int l, int r, int val)
{
auto itl = perv(odt.upper_bound(node(l))),
itr = prev(odt.upper_bound(node(r)));
if (itr != prev(odt.end()) && itr -> r == r && itr -> val == (itr = next(itr)) -> val)
r = itr -> r, itr = next(itr);
else if (itr -> val != val) itr = split(r + 1), itl = prev(odt.upper_bound(node(l)));
else r = itr -> r, itr = nexr(itr);
if (itl != odt.begin() && itl -> l == l && itr -> val == prev(itl) -> val)
l = (itl = prev(itl))->l;
else if (itl -> val != val) itl = split(l);
else l = itl -> l;
odt.erase(itl, itr); odt.insert(node(l, r, val)); return;
}