// get fa, siz, dep, son vector<int> fa(n + 1), siz(n + 1), dep(n + 1), son(n + 1); auto dfs = [&](auto self, int u, int f) -> void { int s = 0; fa[u] = f; siz[u] = 1; dep[u] = dep[f] + 1; for (auto& v : tree[u]) { if (v != f) { self(self, v, u); siz[u] += siz[v]; if (siz[v] > siz[s]) { s = v; son[u] = s; } } } }; dfs(dfs, 1, 0);
// get dfn, top int id = 0; vector<int> top(n + 1), dfn(n + 1); auto get = [&](auto self, int u, int t) -> void { top[u] = t; dfn[u] = ++id; if (son[u]) { self(self, son[u], t); }
for (auto& v : tree[u]) { if (v != fa[u] && v != son[u]) { self(self, v, v); } } }; get(get, 1, 1);// t = 1
int fa[N]; int dep[N]; int siz[N]; int son[N]; int top[N]; vector<int> tree[N];
voiddfs(int u, int f){ fa[u] = f; siz[u] = 1; dep[u] = dep[f] + 1; for (auto v : tree[u]) { if (v != f) { dfs(v, u); siz[u] += siz[v];//计算子节点数 if (siz[son[u]] < siz[v]) son[u] = v;//更新重边 } } }
voidget_top(int u, int f){ top[u] = f; if (son[u]) get_top(son[u], f);//重边 for (auto v : tree[u]) { if (!top[v]) { //# f 不一定是 v 的 父节点 get_top(v, v);//轻边 } } }
// get_top(rt, rt);
intlca(int a, int b){ while (top[a] != top[b]) { //跳到同一条重边 dep[top[a]] > dep[top[b]] ? a = fa[top[a]] : b = fa[top[b]]; } return dep[a] < dep[b] ? a : b;//选择深度更小的节点 }
O(1)
dfn序
$$ lca(x, y) = \begin{cases} x & x = y\ [dfn_{x} + 1, dfn_{y}] 之间深度最小节点的父亲 & x \neq y \end{cases} $$
vector<int> siz(n + 1, 1), son(n + 1); auto dfs = [&](auto dfs, int u, int f) -> void { for (auto& v : tree[u]) { if (v != f) { dfs(dfs, v, u); siz[u] += siz[v]; if (siz[v] >= siz[son[u]]) { son[u] = v; } } } }; dfs(dfs, 1, 0);
i64 result = 0; auto update = [&](auto update, int u, int f, int s) -> void { // 增加点 u 的信息 // 计算result的贡献 for (auto& v : tree[u]) { if (v != f && v != s) { update(update, v, u, s); } } };
auto restore = [&](auto restore, int u, int f) -> void { // 删减点 u 的信息 for (auto& v : tree[u]) { if (v != f) { restore(restore, v, u); } } };
auto calculate = [&](auto calculate, int u, int f, int op) -> void { for (auto& v : tree[u]) { if (v != f && v != son[u]) { calculate(calculate, v, u, 1); } }
if (son[u]) { calculate(calculate, son[u], u, 0); }
update(update, u, f, son[u]); // 获得点 u 答案 -> result
if (op) { restore(restore, u, f); result = 0; } }; calculate(calculate, 1, 0, 0);
TreeHash
判断一些树是否同构的时,我们常常把这些树转成哈希值储存起来,以降低复杂度。 $$ f(S) = \left( c + \sum_{x \in S} g(x) \right) \bmod m $$
structHungary { constint n, m; vector<int> lm, rm; // 左右两边的 match vector<vector<int>> adj; Hungary(int n, int m) : n(n), m(m) { lm.assign(n, -1); rm.assign(m, -1); adj.assign(n, {}); }
// 左边向右边建边 (只用单向边) voidaddEdge(int u, int v){ adj[u].push_back(v); }
intwork(){ int tag = 0; // 时间戳, 这样不用清空 vis vector<int> vis(m); auto dfs = [&](auto dfs, int u) -> bool { for (auto& v : adj[u]) { if (vis[v] != tag) { vis[v] = tag; if (rm[v] == -1 || dfs(dfs, rm[v])) { lm[u] = v; rm[v] = u; returntrue; } } } returnfalse; };
int cnt = 0; for (int i = 0; i < n; i++) { tag += 1; if (dfs(dfs, i)) { cnt += 1; } } return cnt; } };
voidaddEdge(int u, int v, T c){ g[u].push_back(e.size()); e.emplace_back(v, c); g[v].push_back(e.size()); e.emplace_back(u, 0); }
boolbfs(int s, int t){ h.assign(n, -1); h[s] = 0; queue<int> que; que.push(s); while (!que.empty()) { constint u = que.front(); que.pop(); for (constint& i : g[u]) { constauto& [v, c] = e[i]; if (c > 0 && h[v] == -1) { h[v] = h[u] + 1; if (v == t) { returntrue; } que.push(v); } } } returnfalse; }
T dfs(int u, int t, T f){ if (u == t) { return f; } auto r = f; for (int& i = cur[u]; i < int(g[u].size()); i++) { constint j = g[u][i]; auto [v, c] = e[j]; if (c > 0 && h[v] == h[u] + 1) { constauto a = dfs(v, t, min(r, c)); e[j].cap -= a; e[j ^ 1].cap += a; r -= a; if (r == 0) { return f; } } } return f - r; }
T flow(int s, int t){ T ans = 0; while (bfs(s, t)) { cur.assign(n, 0); ans += dfs(s, t, numeric_limits<T>::max()); } return ans; }
vector<bool> minCut(){ vector<bool> c(n); for (int i = 0; i < n; i++) { c[i] = (h[i] != -1); } return c; } structInfo { int from; int to; T cap; T flow; };
// 获取所有边的信息 vector<Info> edges(){ vector<Info> info; for (int i = 0; i < e.size(); i += 2) { Info x; x.from = e[i + 1].to; x.to = e[i].to; x.cap = e[i].cap + e[i + 1].cap; x.flow = e[i + 1].cap; info.push_back(x); } return info; } };
int n, m; cin >> n >> m; vector<vector<int>> g(n); for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; u--, v--; g[u].push_back(v); g[v].push_back(u); }
vector f(n, vector<longlong>(1 << n)); for (int i = 0; i < n; i++) { f[i][1 << i] = 1; }
longlong ans = 0; for (int s = 0; s < (1 << n); s++) { int lowbit = s & (-s); for (int c = 0; c < n; c++) { if (f[c][s] == 0) { continue; } for (auto& v : g[c]) { int mask = (1 << v); if (mask < lowbit) { continue; } if ((s >> v) & 1) { if (lowbit == mask) { ans += f[c][s]; } } else { f[v][s | mask] += f[c][s]; } } } } cout << (ans - m) / 2;
int n, m; cin >> n >> m; vector<int> deg(n + 1); vector<pair<int, int>> edge(m + 1); for (int i = 1; i <= m; i++) { auto& [u, v] = edge[i]; cin >> u >> v; deg[u] += 1; deg[v] += 1; } vector<vector<int>> adj(n + 1); for (int i = 1; i <= m; i++) { auto& [u, v] = edge[i]; if (deg[u] < deg[v] || (deg[u] == deg[v] && u < v)) { adj[u].push_back(v); } else { adj[v].push_back(u); } } int ans = 0; vector<int> vis(n + 1); for (int u = 1; u <= n; u++) { for (auto& v : adj[u]) { vis[v] = true; } for (auto& v : adj[u]) { for (auto& w : adj[v]) { if (vis[w]) { ans += 1; } } } for (auto& v : adj[u]) { vis[v] = false; } } cout << ans;