### Problem Statement You are given a simple conn...
创建于:2025年8月16日
使用 GPT-5 Thinking 回答 Chat01
创建于:2025年8月16日
使用 GPT-5 Thinking 回答 Chat01
You are given a simple connected undirected graph with $N$ vertices numbered $1$ to $N$ and $M$ edges. The $i$-th edge connects vertices $u_i$ and $v_i$.
For each $k=1,2,\ldots,N-1$, find the number of simple paths from vertex $1$ to vertex $N$ that contain exactly $k$ edges.
The input is given from Standard Input in the following format:
text\$N\$ \$M\$ \$u\_1\$ \$v\_1\$ \$u\_2\$ \$v\_2\$ \$\\vdots\$ \$u\_M\$ \$v\_M\$
Output the answers in the following format:
\$\\mathrm{ans}\_1\$ \$\\mathrm{ans}\_2\$ \$\\ldots\$ \$\\mathrm{ans}\_{N-1}\$
$\mathrm{ans}_i$ is the number of simple paths from vertex $1$ to vertex $N$ that contain exactly $i$ edges.
text5 6 1 2 1 3 2 4 3 4 3 5 4 5
0 1 2 1
For each $k=1,2,3,4$, the simple paths from vertex $1$ to vertex $5$ that contain exactly $k$ edges are as follows.
text11 15 1 2 1 3 2 3 3 4 3 5 4 5 5 6 5 7 6 7 7 8 7 9 8 9 9 10 9 11 10 11
0 0 0 0 1 5 10 10 5 1
text7 18 6 7 4 5 1 7 2 7 1 4 2 5 4 6 2 3 5 6 5 7 1 5 2 4 2 6 1 2 1 3 3 4 1 6 3 5
1 3 11 29 50 42
中文c++回答。
下面给出一种适合本题约束(,环路数很少)的做法与 C++ 实现。核心思路是把“绝大多数是链”的图压缩到很小,再在小图上用 DFS 列举所有从 1 到 的简单路径并按长度计数。
设图的回路数(cyclomatic number)。
绝大多数点度数为 2,图“几乎是若干链 + 少量分叉/回边”。
第一步:链压缩(contract degree-2)
把度数为 2 的非端点(既不是 1 也不是 )全部“串联收缩”。
具体做法:把“关键点”定义为:度 或者是 1、。从每个关键点沿边走,直到遇到下一个关键点,整段路径合成一条带权边,权重=经过的边数。这样得到一个多重无向图(允许重边),顶点数 约为 (严格地说,度 的点个数 ),远小于 。
第二步:去叶化(保留 s/t 的 2-core)
在压缩后的图上,把所有度为 1 且不是 / 的点迭代删除(及其相连边)。
这些“挂在旁边的树枝”在简单 路径里不可能出现(因为走进去就出不来而不能重复点),删掉能极大减少分支。
注意:若原图是一条单纯的路径,此时小图只剩一个 的单边(权重为距离),完全没问题。
第三步:在小图上 DFS 枚举简单路径
小图结点 ( 量级),用“按点访问”的 DFS(不允许重复顶点),沿每条边把权重累加成原图中的“边数长度”。到达 时对答案数组 ans[总长度]++
。
由于 , 简单路径总数至多指数于 (大约几百万以内),在 C++ 中完全可承受。
自环(压缩后可能出现)等价于在同一点绕一圈,会重复顶点,不可能出现在简单路径里,直接忽略。
正确性要点
复杂度
long long
储存计数。cpp#include <bits/stdc++.h> using namespace std; struct Edge { int u, v; }; struct CEdge { int a, b; int w; bool alive; }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, M; if (!(cin >> N >> M)) return 0; vector<Edge> E(M); for (int i = 0; i < M; ++i) { int u, v; cin >> u >> v; --u; --v; E[i] = {u, v}; } // 原图:按边索引存邻接 vector<vector<int>> inc(N); vector<int> deg(N, 0); for (int i = 0; i < M; ++i) { inc[E[i].u].push_back(i); inc[E[i].v].push_back(i); deg[E[i].u]++; deg[E[i].v]++; } auto other = [&](int eid, int x) { return E[eid].u ^ E[eid].v ^ x; }; // 关键点:度!=2 或者 是 s(0)/t(N-1) int s0 = 0, t0 = N - 1; vector<char> is_key(N, false); for (int v = 0; v < N; ++v) { if (deg[v] != 2 || v == s0 || v == t0) is_key[v] = true; } // 给关键点编号 vector<int> cid(N, -1); int K = 0; for (int v = 0; v < N; ++v) if (is_key[v]) cid[v] = K++; // 从每个关键点出发,把链收缩为带权边 vector<char> usedE(M, false); vector<CEdge> cedges; cedges.reserve(M); for (int u = 0; u < N; ++u) if (is_key[u]) { for (int eid : inc[u]) if (!usedE[eid]) { usedE[eid] = true; int prev = u; int cur = other(eid, u); int len = 1; int last_eid = eid; while (!is_key[cur]) { // cur 度必为 2,沿另一条边走 int e0 = inc[cur][0], e1 = inc[cur][1]; int ne = (e0 == last_eid ? e1 : e0); usedE[ne] = true; last_eid = ne; int nxt = other(ne, cur); prev = cur; cur = nxt; len++; } int a = cid[u], b = cid[cur]; if (a != b) { // 自环无效,直接丢弃 cedges.push_back({a, b, len, true}); } } } if (K == 0) { // 所有点都不是关键点?只有可能 N=2 且一条边被自环丢弃的极端,按题意连通不会发生 // 兜底:输出全 0 for (int k = 1; k <= N - 1; ++k) cout << 0 << (k==N-1?'\n':' '); return 0; } int s = cid[s0], t = cid[t0]; // 小图邻接(带边编号) int CE = (int)cedges.size(); vector<vector<pair<int,int>>> adj(K); // (eid, to) vector<int> degC(K, 0); for (int i = 0; i < CE; ++i) { auto [a,b,w,alive] = cedges[i]; adj[a].push_back({i, b}); adj[b].push_back({i, a}); degC[a]++; degC[b]++; } // 去叶化:删除所有度=1且不是 s/t 的点(迭代) vector<char> removed(K, false); queue<int> q; for (int v = 0; v < K; ++v) { if (v == s || v == t) continue; if (degC[v] == 1) q.push(v); } while (!q.empty()) { int v = q.front(); q.pop(); if (removed[v] || v == s || v == t) continue; if (degC[v] != 1) continue; removed[v] = true; for (auto [eid, to] : adj[v]) if (cedges[eid].alive) { cedges[eid].alive = false; degC[v]--; degC[to]--; if (to != s && to != t && !removed[to] && degC[to] == 1) q.push(to); } } // DFS 枚举简单路径(按点去重) vector<long long> ans(max(1, N), 0LL); vector<char> vis(K, false); vis[s] = true; function<void(int,int)> dfs = [&](int u, int dist) { if (u == t) { if (dist >= 0 && dist <= N-1) ans[dist]++; return; } for (auto [eid, v] : adj[u]) { if (!cedges[eid].alive) continue; if (removed[v]) continue; if (vis[v]) continue; int w = cedges[eid].w; if (dist + w > N-1) continue; // 理论上不会超,但保险 vis[v] = true; dfs(v, dist + w); vis[v] = false; } }; // 若 s 或 t 被完全孤立(无路),DFS 也会给出全 0 dfs(s, 0); // 输出 for (int k = 1; k <= N - 1; ++k) { if (k < (int)ans.size()) cout << ans[k]; else cout << 0; cout << (k == N-1 ? '\n' : ' '); } return 0; }
long long
足够(在本题 的范围内,路径总数远小于 )。