[学习笔记] 割点 & 割边 & 双连通分量
一、定义
在 无向连通图
若无向连通图
若无向连通图
点双连通分量 (biconnected component) 是指极大点双连通子图。
边双连通分量 (
二、Tarjan 求割边
很简单,如果 DFS 树的一个子树满足其中的返祖边不足以跳出这个子树,那么这个子树的根与其父亲之间的边就是割边。
下面给出此题代码:
/**
* author : OMG_78
* created: 2023-07-12-10.45.42
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 2e5 + 5;
vector < pair < int, int > > G[N];
int ed[N][2];
int dfn[N], low[N], ck, n, m;
vector < int > bri;
inline void tarjan (int u, int eid) {
dfn[u] = low[u] = ++ ck;
for (int i = 0;i < G[u].size (); ++ i) {
int v = G[u][i].first, eid2 = G[u][i].second;
if (!dfn[v]) tarjan (v, eid2);
if (eid != eid2) low[u] = min (low[u], low[v]);
}
if (dfn[u] == low[u] && eid != -1) bri.push_back (eid);
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n), read (m);
for (int i = 1;i <= m; ++ i) {
read (ed[i][0]), read (ed[i][1]);
G[ed[i][0]].push_back (make_pair (ed[i][1], i));
G[ed[i][1]].push_back (make_pair (ed[i][0], i));
}
for (int i = 1;i <= n; ++ i) {
if (!dfn[i]) tarjan (i, -1);
}
printf ("%d\n", bri.size ());
sort (bri.begin (), bri.end ());
for (int i = 0;i < bri.size (); ++ i) {
printf ("%d %d\n", ed[bri[i]][0], ed[bri[i]][1]);
}
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
三、Tarjan 求割点
思路及其类似,如果
/**
* author : OMG_78
* created: 2023-07-12-11.19.33
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 1e5 + 5;
int dfn[N], low[N], vis[N], cut[N];
int res, ck, n, m;
vector < int > G[N];
inline void tarjan (int u, int fa) {
vis[u] = 1;
dfn[u] = low[u] = ++ ck;
int son = 0;
for (int v : G[u]) {
if (!vis[v]) {
son ++;
tarjan (v, u);
low[u] = min (low[u], low[v]);
if (fa != u && low[v] >= dfn[u] && !cut[u]) {
cut[u] = 1; res ++;
}
}
else if (v != fa) {
low[u] = min (low[u], dfn[v]);
}
}
if (fa == u && son > 1 && !cut[u]) {
cut[u] = 1; res ++;
}
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n), read (m);
for (int i = 1;i <= m; ++ i) {
int u, v;
read (u), read (v);
G[u].push_back (v);
G[v].push_back (u);
}
for (int i = 1;i <= n; ++ i) {
if (!vis[i]) {
ck = 0;
tarjan (i, i);
}
}
printf ("%d\n", res);
for (int i = 1;i <= n; ++ i) {
if (cut[i]) printf ("%d ", i);
}
printf ("\n");
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
四、边双连通分量
边双连通具有传递性。
性质:一张无向图缩完边双之后是 一棵树。
/**
* author : OMG_78
* created: 2023-07-13-10.49.45
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 5e5 + 5, M = 2e6 + 5;
int n, m, eid = 1, ans, ck, dfn[N], low[N], head[N], bel[N];
struct Edge {int to, nxt;} ed[M << 1];
bool vis[M << 1];
typedef vector < int > vint;
vector < vint > st;
inline void add_edge (int u, int v) {
eid ++;
ed[eid].to = v;
ed[eid].nxt = head[u];
head[u] = eid;
}
inline void tarjan (int u, int id) {
dfn[u] = low[u] = ++ ck;
for (int i = head[u]; i ; i = ed[i].nxt) {
int v = ed[i].to;
if (!dfn[v]) {
tarjan (v, i);
if (dfn[u] < low[v]) vis[i] = vis[i ^ 1] = 1;
low[u] = min (low[u], low[v]);
}
else if (i != (id ^ 1)) low[u] = min (low[u], dfn[v]);
}
}
inline void dfs (int u, int blo) {
bel[u] = blo;
st[blo].push_back (u);
for (int i = head[u]; i ; i = ed[i].nxt) {
int v = ed[i].to;
if (bel[v] || vis[i]) continue;
dfs (v, blo);
}
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n), read (m);
for (int i = 1;i <= m; ++ i) {
int u, v;
read (u), read (v);
add_edge (u, v);
add_edge (v, u);
}
for (int i = 1;i <= n; ++ i) {
if (!dfn[i]) tarjan (i, 0);
}
st.push_back (vint ());
for (int i = 1;i <= n; ++ i) {
if (!bel[i]) st.push_back (vint ()), ans ++, dfs (i, ans);
}
printf ("%d\n", ans);
for (int i = 1;i <= ans; ++ i) {
sort (st[i].begin (), st[i].end ());
printf ("%d ", st[i].size ());
for (int j = 0;j < st[i].size (); ++ j) printf ("%d ", st[i][j]);
printf ("\n");
}
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
五、点双连通分量
点双连通不具有传递性。
/**
* author : OMG_78
* created: 2023-07-13-10.36.27
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 5e5 + 5, M = 2e6 + 5;
int stk[M << 1], tp, cnt, low[N], dfn[N], ck, n, m;
vector < int > st[N], G[N];
inline void tarjan (int u, int fa) {
int son = 0;
low[u] = dfn[u] = ++ ck;
stk[++ tp] = u;
for (int v : G[u]) {
if (!dfn[v]) {
son ++;
tarjan (v, u);
low[u] = min (low[u], low[v]);
if (low[v] >= dfn[u]) {
cnt ++;
while (stk[tp + 1] != v) st[cnt].push_back (stk[tp --]);
st[cnt].push_back (u);
}
}
else if (v != fa) low[u] = min (low[u], dfn[v]);
}
if (!fa && !son) st[++ cnt].push_back (u);
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n), read (m);
for (int i = 1;i <= m; ++ i) {
int u, v;
read (u), read (v);
G[u].push_back (v);
G[v].push_back (u);
}
for (int i = 1;i <= n; ++ i) {
if (!dfn[i]) {
tp = 0;
tarjan (i, 0);
}
}
printf ("%d\n", cnt);
for (int i = 1;i <= cnt; ++ i) {
sort (st[i].begin (), st[i].end ());
printf ("%d ", st[i].size ());
for (int j = 0;j < st[i].size (); ++ j) printf ("%d ", st[i][j]);
printf ("\n");
}
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
六、洛谷 P2860 [USACO06JAN] Redundant Paths G
https://www.luogu.com.cn/problem/P2860
简化题意:给定一张无向连通图,求最少要加多少条边才能使得原图没有割边。
首先我们要缩边双,然后发现这是一棵树,容易发现只要把叶子节点都两两连起来,就没有割边了(因为形成了环),你在非叶子节点加边显然 不能最优的 消除所有割边。
所以答案就是
/**
* author : OMG_78
* created: 2023-07-13-10.49.45
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 5e3 + 5, M = 1e4 + 5;
int n, m, eid = 1, ans, ck, dfn[N], low[N], head[N], bel[N], mp[N], bj[N];
struct Edge {int to, nxt;} ed[M << 1];
vector < PII > eds;
bool vis[M << 1];
typedef vector < int > vint;
vector < vint > st;
inline void add_edge (int u, int v) {
eid ++;
ed[eid].to = v;
ed[eid].nxt = head[u];
head[u] = eid;
}
inline void tarjan (int u, int id) {
dfn[u] = low[u] = ++ ck;
for (int i = head[u]; i ; i = ed[i].nxt) {
int v = ed[i].to;
if (!dfn[v]) {
tarjan (v, i);
if (dfn[u] < low[v]) vis[i] = vis[i ^ 1] = 1;
low[u] = min (low[u], low[v]);
}
else if (i != (id ^ 1)) low[u] = min (low[u], dfn[v]);
}
}
inline void dfs (int u, int blo) {
bel[u] = blo;
st[blo].push_back (u);
for (int i = head[u]; i ; i = ed[i].nxt) {
int v = ed[i].to;
if (bel[v] || vis[i]) continue;
dfs (v, blo);
}
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n), read (m);
for (int i = 1;i <= m; ++ i) {
int u, v;
read (u), read (v);
add_edge (u, v);
add_edge (v, u);
eds.push_back (make_pair (u, v));
}
for (int i = 1;i <= n; ++ i) {
if (!dfn[i]) tarjan (i, 0);
}
st.push_back (vint ());
for (int i = 1;i <= n; ++ i) {
if (!bel[i]) st.push_back (vint ()), ans ++, dfs (i, ans);
}
for (int i = 1;i <= ans; ++ i) {
for (int j = 0;j < st[i].size (); ++ j) mp[st[i][j]] = i;
}
set < PII > stqwq;
stqwq.clear ();
for (PII e : eds) {
int u = e.first, v = e.second;
u = mp[u], v = mp[v];
if (u != v) stqwq.insert (make_pair (u, v));
}
for (PII e : stqwq) bj[e.first] ++, bj[e.second] ++;
int leaf = 0;
for (int i = 1;i <= n; ++ i) leaf += (bj[i] == 1);
leaf = (leaf + 1) / 2;
printf ("%d\n", leaf);
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
七、洛谷 P8867 [NOIP2022] 建造军营
https://www.luogu.com.cn/problem/P8867
边双连通分量 + 树形 DP 好题。
参考了你谷第一篇题解。()
容易发现 B 国只有摧毁了 A 国的割边才可能阻断两个军营的联系。
所以考虑将原图缩边双,然后形成一棵树,设缩完点之后节点
既然缩点之后是一棵树,那么考虑树形 dp。
设
如果
如果
反之就随便:方案数乘上
整理一下有:
按顺序 dp(
考虑一下边界,
设
考虑统计答案:容易发现建造的军营都可以被包含在一个子树中,设为
注意常数,这个 sb 写的时候被卡常了。
/**
* author : OMG_78
* created: 2023-07-14-09.18.17
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 5e5 + 5, M = 1e6 + 5;
const ll mod = 1e9 + 7;
int n, m, eid = 1, ck, dfn[N], low[N], head[N], bel[N], cnts;
struct Edge {int to, nxt;} ed[M << 1];
bool vis[M << 1];
typedef vector < int > vint;
vector < vint > st;
inline void add_edge (int u, int v) {
eid ++;
ed[eid].to = v;
ed[eid].nxt = head[u];
head[u] = eid;
}
inline void tarjan (int u, int id) {
dfn[u] = low[u] = ++ ck;
for (int i = head[u]; i ; i = ed[i].nxt) {
int v = ed[i].to;
if (!dfn[v]) {
tarjan (v, i);
if (dfn[u] < low[v]) vis[i] = vis[i ^ 1] = 1;
low[u] = min (low[u], low[v]);
}
else if (i != (id ^ 1)) low[u] = min (low[u], dfn[v]);
}
}
inline void dfs (int u, int blo) {
bel[u] = blo;
st[blo].push_back (u);
for (int i = head[u]; i ; i = ed[i].nxt) {
int v = ed[i].to;
if (bel[v] || vis[i]) continue;
dfs (v, blo);
}
}
vector < int > tr[N];
int E[N], V[N], sum[N];
ll dp[N][2], ans, pw[1500025];
inline void pre (int u, int fa) {
sum[u] = E[u];
for (int v : tr[u]) {
if (v == fa) continue;
pre (v, u);
sum[u] += sum[v] + 1;
}
}
inline void solve (int u, int fa) {
for (int v : tr[u]) {
if (v == fa) continue;
solve (v, u);
dp[u][1] = (dp[u][0] * dp[v][1] + dp[u][1] * (2 * dp[v][0] + dp[v][1])) % mod;
dp[u][0] = (dp[u][0] * dp[v][0] * 2) % mod;
}
if (u == 1) ans += dp[u][1];
else ans = (ans + dp[u][1] * pw[sum[1] - sum[u] - 1]) % mod;
}
vector < PII > edqwq;
bool Memory_Ends;
signed main () {
// freopen ("barrack4.in", "r", stdin);
// freopen ("barrack4.out", "w", stdout);
// fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
pw[0] = 1;
for (int i = 1;i < 1500025; ++ i) pw[i] = pw[i - 1] * 2 % mod;
read (n), read (m);
for (int i = 1;i <= m; ++ i) {
int u, v;
read (u), read (v);
add_edge (u, v);
add_edge (v, u);
edqwq.push_back (make_pair (u, v));
}
for (int i = 1;i <= n; ++ i) {
if (!dfn[i]) tarjan (i, 0);
}
st.push_back (vint ());
for (int i = 1;i <= n; ++ i) {
if (!bel[i]) st.push_back (vint ()), cnts ++, dfs (i, cnts);
}
set < PII > st;
st.clear ();
for (PII qwq : edqwq) {
int u = qwq.first;
int v = qwq.second;
u = bel[u], v = bel[v];
if (u != v) {
st.insert (make_pair (u, v));
st.insert (make_pair (v, u));
}
else E[u] ++;
}
for (PII qwq : st) {
int u = qwq.first;
int v = qwq.second;
tr[u].push_back (v);
}
for (int i = 1;i <= n; ++ i) V[bel[i]] ++;
for (int i = 1;i <= cnts; ++ i) {
dp[i][0] = pw[E[i]];
dp[i][1] = (pw[E[i] + V[i]] - dp[i][0]) % mod;
dp[i][1] = (dp[i][1] + mod) % mod;
}
pre (1, 0);
solve (1, 0);
printf ("%lld\n", ans % mod);
// fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
八、洛谷 P3469 [POI2008] BLO-Blockade
https://www.luogu.com.cn/problem/P3469
只需要在跑 tarjan 的时候再加上一个步骤就行了。
容易发现一个大小为
然后在判
如果
如果
注意要把贡献加全,别忘了开 long long。
/**
* author : OMG_78
* created: 2023-07-14-11.57.36
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
const int N = 1e5 + 5;
int dfn[N], low[N], vis[N], cut[N];
ll siz[N];
ll ans[N];
int res, ck, n, m;
vector < int > G[N];
inline void tarjan (int u, int fa) {
vis[u] = siz[u] = 1;
dfn[u] = low[u] = ++ ck;
int son = 0;
ll solved = 1;
for (int v : G[u]) {
if (!vis[v]) {
son ++;
tarjan (v, u);
siz[u] += siz[v];
low[u] = min (low[u], low[v]);
if (low[v] >= dfn[u]) {
cut[u] = 1; res ++;
ans[u] += siz[v] * ((1ll * n) - siz[v]);
solved += siz[v];
}
}
else if (v != fa) {
low[u] = min (low[u], dfn[v]);
}
}
if (fa == u && son > 1 && !cut[u]) {
cut[u] = 1; res ++;
}
if (!cut[u]) ans[u] = 2 * (n - 1);
else ans[u] += (1ll * n - 1ll) + solved * (1ll * n - solved);
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n), read (m);
for (int i = 1;i <= m; ++ i) {
int u, v;
read (u), read (v);
G[u].push_back (v);
G[v].push_back (u);
}
for (int i = 1;i <= n; ++ i) {
if (!vis[i]) {
ck = 0;
tarjan (i, i);
}
}
for (int i = 1;i <= n; ++ i) printf ("%lld\n", ans[i]);
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
九、洛谷 P3225 [HNOI2012] 矿场搭建
https://www.luogu.com.cn/problem/P3225
2 倍经验:https://www.luogu.com.cn/problem/SP16185
3 倍经验:https://www.luogu.com.cn/problem/UVA1108
提示:
站外双倍经验数据范围的
站外双倍经验数据范围的
站外双倍经验数据范围的
对于每一个点双连通分量分类讨论:
-
没有割点。建
个,防止其中 个崩塌。 -
有
个割点。建 个,这个出口崩塌可以通过割点跑到别的点双连通分量,否则通过这个出口逃跑。 -
有
个割点。不建,至少有 个割点可以跑到别的点双连通分量。
然后乘法原理计数即可,记得开 unsigned long long。
/**
* author : OMG_78
* created: 2023-07-14-12.37.18
**/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
if (c < '0') return true;
if (c > '9') return true;
return false;
}
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
int n, m;
namespace cut_vertex {
const int N = 5e4 + 25;
int dfn[N], low[N], vis[N], cut[N];
int res, ck;
vector < int > G[N];
inline void cls () {
for (int i = 1;i <= 5e4 + 10; ++ i) dfn[i] = low[i] = vis[i] = cut[i] = 0;
res = ck = 0;
for (int i = 1;i <= 5e4 + 10; ++ i) G[i].clear ();
}
inline void tarjan (int u, int fa) {
vis[u] = 1;
dfn[u] = low[u] = ++ ck;
int son = 0;
for (int v : G[u]) {
if (!vis[v]) {
son ++;
tarjan (v, u);
low[u] = min (low[u], low[v]);
if (fa != u && low[v] >= dfn[u] && !cut[u]) {
cut[u] = 1; res ++;
}
}
else if (v != fa) {
low[u] = min (low[u], dfn[v]);
}
}
if (fa == u && son > 1 && !cut[u]) {
cut[u] = 1; res ++;
}
}
inline set < int > solve () {
for (int i = 1;i <= n; ++ i) {
if (!vis[i]) {
ck = 0;
tarjan (i, i);
}
}
set < int > ans;
ans.clear ();
for (int i = 1;i <= n; ++ i) {
if (cut[i]) ans.insert (i);
}
return ans;
}
}
namespace biconnected_component {
const int N = 5e4 + 25, M = 5e4 + 5;
int stk[M << 1], tp, cnt, low[N], dfn[N], ck;
vector < int > st[N], G[N];
inline void cls () {
for (int i = 0;i < (M << 1); ++ i) stk[i] = 0;
for (int i = 1;i <= 5e4 + 10; ++ i) low[i] = dfn[i] = 0;
tp = cnt = ck = 0;
for (int i = 1;i <= 5e4 + 10; ++ i) st[i].clear (), G[i].clear ();
}
inline void tarjan (int u, int fa) {
int son = 0;
low[u] = dfn[u] = ++ ck;
stk[++ tp] = u;
for (int v : G[u]) {
if (!dfn[v]) {
son ++;
tarjan (v, u);
low[u] = min (low[u], low[v]);
if (low[v] >= dfn[u]) {
cnt ++;
while (stk[tp + 1] != v) st[cnt].push_back (stk[tp --]);
st[cnt].push_back (u);
}
}
else if (v != fa) low[u] = min (low[u], dfn[v]);
}
if (!fa && !son) st[++ cnt].push_back (u);
}
inline void solve () {
for (int i = 1;i <= n; ++ i) {
if (!dfn[i]) {
tp = 0;
tarjan (i, 0);
}
}
}
}
int problemprovidercreep;
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
while (true) {
problemprovidercreep ++;
read (m);
if (!m) break;
cut_vertex :: cls ();
biconnected_component :: cls ();
n = 0;
for (int i = 1;i <= m; ++ i) {
int u, v;
read (u), read (v);
cut_vertex :: G[u].push_back (v);
cut_vertex :: G[v].push_back (u);
biconnected_component :: G[u].push_back (v);
biconnected_component :: G[v].push_back (u);
n = max (n, u);
n = max (n, v);
}
set < int > ve = cut_vertex :: solve ();
unsigned long long ans2 = 1;
int ans1 = 0;
biconnected_component :: solve ();
for (int i = 1;i <= biconnected_component :: cnt; ++ i) {
int ct = 0;
for (int v : (biconnected_component :: st[i])) {
if (ve.count (v)) ct ++;
}
if (ct == 1) {
ans1 ++;
int tmp = (biconnected_component :: st[i]).size ();
tmp --;
unsigned long long kd = (unsigned long long) tmp;
ans2 *= kd;
}
if (!ct) {
ans1 += 2;
int tmp = (biconnected_component :: st[i]).size ();
unsigned long long kd = (unsigned long long) tmp;
tmp --;
kd *= (unsigned long long) tmp;
kd >>= 1;
ans2 *= kd;
}
}
printf ("Case %d: %d %llu\n", problemprovidercreep, ans1, ans2);
}
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】