题解:P12799 [NERC 2022] Jumbled Trees
提供一个至多 \(m\) 次的做法。
一个经典结论是,任取无向连通图 \(G\) 的两棵生成树 \(T_1,T_2\),则 \(T_1\) 必然可以通过若干次“加入一条非树边,删去一条树边”的操作变成 \(T_2\)。
证明
显然存在一条 \(T_2\) 中的树边 \(e\),使得 \(e\) 为 \(T_1\) 中的非树边。\(e\) 与对应的一些树边在 \(T_1\) 中会构成一个环,这个环中又会存在一条 \(T_1\) 中的树边 \(f\),使得 \(f\) 在 \(T_2\) 中为非树边(否则整个环上的边都是 \(T_2\) 中的非树边),我们令 \(T_1\gets T_1\setminus\{f\}\cup\{e\}\),则 \(T_1\) 与 \(T_2\) 的公共边数量恰好增加 \(1\)。不断对 \(T_1\) 做这样的操作,\(T_1\) 最终必然会变成 \(T_2\)。\(\Box\)
任取 \(G\) 的一棵生成树 \(T\),那么每次操作都可以分解成:对 \(T\) 做一次 \(+v\) 的操作,再做若干次“给一条边 \(+v\),另一条边 \(-v\)”的操作,下文中称为交换操作。并非任意两条边都可以做交换操作,必须存在某棵生成树 \(T'\),使得其中一条边是树边,另一条是非树边,因此这两条边必须同处于一个环中。不妨将所有边划分成若干等价类,对于 \(T\) 外的每条非树边 \((u,v)\),将其与 \(\operatorname{path}(u,v)\) 上的所有树边所在的等价类合并。这样同一个等价类里的任意两条边都可以执行交换操作。可以发现一个性质:对于一个等价类 \(S\),任意一棵生成树 \(T\) 与 \(S\) 的交集大小是固定的。设所有操作的 \(v\) 之和为 \(D\),对于一个等价类 \(S\),设 \(|T\cap S|\) 这个固定的值为 \(c_S\),则可以得到条件:
若存在 \(S\) 使得 \(c_S\bmod{p}=0\land \sum\limits_{e\in S}x_e\bmod{p}\neq 0\),则显然无解。对于每个 \(S\),都可以解出一个 \(D\),若这些 \(D\) 不全部相同,则同样无解;否则我们可以直接确定 \(D\) 的取值。
我们得到了一些判定有解的必要条件,猜测这也是充分的,尝试给出构造。先对 \(T\) 做一次 \(+D\) 的操作,那么一条边 \(e\) 还需要补上的权值为
注意到此时一个等价类 \(S\) 内总的需求量恰好为
在合并等价类时,我们每次合并的都是 \(T\) 中的一条树边和一条非树边,对这个合并的过程提取出一个树结构,每个点对应原图中的一条边,每条边连接一条树边和一条非树边。对这个树结构自底向上递归操作,考虑当前的一对节点 \((u,fa_u)\),若 \(fa_u\) 为非树边,\(u\) 为树边,则我们对 \(T\) 做一次 \(+x_u'\) 的操作,再对 \(T\setminus\{u\}\cup\{fa_u\}\) 做一次 \(-x_u'\) 的操作,则 \(u\) 的权值恰好被补到了 \(x_u\),再令 \(x'_{fa_u}\gets x'_{fa_u}+x'_u\) 即可。另一种情况也是类似的。记录对 \(T\) 进行操作的总权值,最后输出即可。
这样我们就完成了构造,操作次数为 \(m+1-c\leq m\),其中 \(c\) 为等价类个数。时间复杂度为 \(\mathcal{O}(nm\log{m})/\mathcal{O}(nm\alpha(m))\)。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 505, MAXM = 1005;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
int n, m, mod, base;
int dep[MAXN], fa[MAXN], faId[MAXN];
int val[MAXM], cnt[MAXM], sum[MAXM];
bool visT[MAXM];
pii edges[MAXM];
vector<pii> G[MAXN];
vector<int> T[MAXM], edgesT;
template<typename T> T add(T x, T y) { return (x += y) >= mod ? x - mod : x; }
template<typename T> T sub(T x, T y) { return (x -= y) < 0 ? x + mod : x; }
template<typename T> void cadd(T &x, T y) { (x += y) >= mod ? (x -= mod) : x; }
template<typename T> void csub(T &x, T y) { (x -= y) < 0 ? (x += mod) : x; }
int qpow(int a, int b) {
int res = 1;
for (; b; b >>= 1) {
if (b & 1) res = (ll)res * a % mod;
a = (ll)a * a % mod;
}
return res;
}
struct DSU {
int fa[MAXM];
void init(int n) { iota(fa + 1, fa + n + 1, 1); }
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
void unite(int x, int y) { fa[find(x)] = find(y); }
} dsu;
void dfs1(int u) {
for (auto [v, id] : G[u]) {
if (id == faId[u] || dep[v]) continue;
visT[id] = 1;
fa[v] = u;
faId[v] = id;
dep[v] = dep[u] + 1;
dfs1(v);
}
}
void dfs2(int u, int faU) {
for (int v : T[u]) {
if (v == faU) continue;
dfs2(v, u);
int c = visT[v] ? sub(0, val[v]) : val[v];
cout << c << ' ';
for (int e : edgesT) {
if (e == u || e == v) cout << (e == u ? v : u) << ' ';
else cout << e << ' ';
}
cout << '\n';
csub(base, c);
cadd(val[u], val[v]);
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> mod;
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v >> val[i];
G[u].emplace_back(v, i);
G[v].emplace_back(u, i);
edges[i] = {u, v};
}
dep[1] = 1;
dfs1(1);
dsu.init(m);
for (int i = 1; i <= m; ++i) {
if (visT[i]) {
edgesT.emplace_back(i);
continue;
}
auto [u, v] = edges[i];
if (dep[u] > dep[v]) swap(u, v);
while (v != u) {
int id = faId[v];
if (dsu.find(id) != dsu.find(i)) {
dsu.unite(id, i);
T[id].emplace_back(i);
T[i].emplace_back(id);
}
v = fa[v];
}
}
vector<int> root;
for (int i = 1; i <= m; ++i) {
int rt = dsu.find(i);
if (i == rt) root.emplace_back(rt);
if (visT[i]) cadd(cnt[rt], 1);
cadd(sum[rt], val[i]);
}
int D = -1;
for (int i : root) {
if (!cnt[i]) {
if (sum[i]) return cout << "-1", 0;
else continue;
}
int val = (ull)sum[i] * qpow(cnt[i], mod - 2) % mod;
if (~D && D != val) return cout << "-1", 0;
D = val;
}
if (!~D) D = 0;
base = D;
for (int id : edgesT) csub(val[id], D);
cout << m - root.size() + 1 << '\n';
for (int i : root) dfs2(i, 0);
cout << base << ' ';
for (int e : edgesT) cout << e << ' ';
return 0;
}

浙公网安备 33010602011771号