题解:P15653 [省选联考 2026] 星图 / starmap
这也太难了,场上一分没拿到 /ll
谨以这篇题解纪念我死透了的高一赛季。
提供一种异或四元环不需要找欧拉回路的做法。
取原图的补图,转化为最小化边数。
考虑构造一些基础结构。我们发现可以用 \(4\) 次操作异或上一个四元环 \((a,b),(b,c),(c,d),(d,a)\)。具体来说,取 \(k-2\) 个异于 \(a,b,c,d\) 的点 \(x_{1\sim k-2}\),执行下面的操作即可:
接下来,考察一个满足如下条件的图:
- \(2\mid |E|\)。
- \(\forall 1\leq i\leq n,2\mid deg_i\)。
我们指出,这样的图必然可以通过若干次异或四元环的操作消成空图。
证明
直接给出构造。
在此之前,我们不妨先考察三元环集合 \(S=\{(1,u),(u,v),(v,1)\mid (u,v)\in E\land u\neq 1\land v\neq 1\}\),该集合内的三元环的异或和恰好就是 \(G\)。这是因为,如果令 \(G'=G\oplus S\),那么 \(G'\) 中点的度数也都是偶数,而显然 \(G'\) 中不含两端都不为 \(1\) 的边,因此 \(G'\) 就是空图。
容易发现 \(S\) 的大小是偶数,考虑给三元环两两配对。对于每对三元环 \((1,a),(a,b),(b,1)\) 和 \((1,c),(c,d),(d,1)\),分类讨论:
- 若这两个三元环存在恰好一条公共边,那么这两个三元环异或起来恰好就是一个四元环。
- 若这两个三元环不存在公共边,也就是说 \(a,b,c,d\) 互不相同,那么我们可以将其分解成两个四元环 \((1,a),(a,b),(b,c),(c,1)\) 和 \((1,b),(b,c),(c,d),(d,1)\) 的异或和。
这样我们就得到了构造方案。\(\Box\)
由上述结论,我们发现最小化最终边数,等价于求:最少改变 \(G\) 中多少条边的状态,使得 \(G\) 的边数和每个点的度数都为偶数。
考虑寻找一些不变量:
- 当 \(2\mid (k-1)\) 时,度数的奇偶性不变。
- 当 \(2\mid \dbinom k2\) 时,边数的奇偶性不变。
反之:
- 当 \(2\nmid (k-1)\) 时,可以通过执行 \((x_1,\cdots,x_{k-1},u)\) 和 \((x_1,\cdots,x_{k-1},v)\) 这 \(2\) 次操作来改变 \(deg_u,deg_v\) 的奇偶性。
- 当 \(2\nmid \dbinom k2\) 时,我们随便执行一次操作即可改变边数的奇偶性。
直接分讨:
- \(2\nmid (k-1)\land 2\nmid \dbinom k2\Leftrightarrow k\bmod{4}=2\):无需翻转任何边。
- \(2\nmid (k-1)\land 2\mid \dbinom k2\Leftrightarrow k\bmod{4}=0\):若边数为奇数,则需要任意翻转一条边。
- \(2\mid (k-1)\land 2\nmid \dbinom k2\Leftrightarrow k\bmod{4}=3\):将奇度点两两配对翻转,也就是说,设有 \(cnt\) 个奇度点,则需要翻转 \(\dfrac {cnt}2\) 条边。
- \(2\mid (k-1)\land 2\mid \dbinom k2\Leftrightarrow k\bmod{4}=1\):还是先将奇度点两两配对翻转。若操作后图的边数 \(m-\dfrac{cnt} 2\) 为奇数,则需要在不改变度数奇偶性的前提下,改变边数奇偶性。容易想到翻转一个三元环,注意三元环可以和原先的翻转边异或起来,因此这里需要进一步讨论:
- \(cnt=0\):必须完整地翻转一个三元环,则 \(ans\gets ans-3\)。
- \(cnt>0\):可以让三元环包含一条翻转边,则 \(ans\gets ans-1\)。
这样我们就会计算答案了,可以获得 \(25\text{ pts}\)。
考虑如何构造方案。注意操作次数上限为 \(\dbinom n2\) 次,这个限制看起来比较烦。
不妨先考虑 \(n=k+2\) 怎么做。此时只有 \(\dbinom nk=\dbinom n2\) 种本质不同的操作,这意味着我们其实可以任意操作。直接按照前文中提到的方法来做:
- 若边数为奇数,则随便进行一次操作。
- 将奇度点两两匹配,每对奇度点用两次操作改变奇偶性。
- 最后将所有不含 \(1\) 的边两两匹配,分解成 \(1\sim 2\) 次异或四元环操作。
过程中记录每种操作被操作次数的奇偶性即可。
接下来做 \(n>k+2\)。考虑直接归纳,把 \(n\) 号点删掉,递归变为子问题。为了保证总操作次数不超过 \(\dbinom n2\) 次,一个规模为 \(n\) 的问题至多只能操作 \(n-1\) 次。不妨考虑 \(n\) 的所有邻点,将邻点两两匹配,对于每一对邻点 \(u,v\),可以通过执行 \((x_1,\cdots,x_{k-2},n,u)\) 和 \((x_1,\cdots,x_{k-2},n,v)\) 这两次操作来断开 \((n,u)\) 和 \((n,v)\) 两条边。那如果 \(deg_n\) 为奇数怎么办呢?考虑在配对之前直接调整,注意到 \(deg_n\) 为奇数时必然有 \(2\mid (k-1)\),那么执行 \((x_1,\cdots,x_{k-1},n)\) 这一操作即可改变 \(deg_n\) 的奇偶性。这里有细节,为了不让这一次额外操作导致操作次数 \(>n-1\),我们需要保证执行额外操作后,\(n\) 的邻点个数 \(<n-1\) 个。这是简单的,执行额外操作时强制选上 \(n\) 的任意一个邻点即可。
这样我们就以至多 \(\dbinom n2\) 次操作解决了本题。
直接模拟上述过程即可,代码其实不算难写。
代码
void starmap(int n, int m, int k, int p, vector<int> u, vector<int> v) {
for (int i = 1; i <= n; ++i) {
A[i].reset(), op[i].reset();
for (int j = 1; j <= n; ++j) A[i][j] = i != j;
}
for (int i = 0; i < m; ++i) A[u[i]][v[i]] = A[v[i]][u[i]] = 0;
int tot = n * (n - 1) >> 1;
m = tot - m;
auto flip = [&](int u, int v) { A[u].flip(v), A[v].flip(u); };
if (k % 4 == 0) {
int ans = tot;
if (m & 1) --ans, flip(1, 2);
report(ans);
} else if (k % 4 == 2) report(tot);
else {
vector<int> O;
for (int i = 1; i <= n; ++i) if (A[i].count() & 1) O.emplace_back(i);
int ans = tot;
for (int i = 0; i < O.size(); i += 2) --ans, flip(O[i], O[i + 1]);
if (k % 4 == 3) report(ans);
else {
int cnt = O.size() >> 1;
if (m - cnt & 1) {
int x, y, z;
if (cnt) {
x = O[0], y = O[1], z = 1;
while (z == x || z == y) ++z;
--ans;
} else {
x = 1, y = 2, z = 3;
ans -= 3;
}
flip(x, y), flip(y, z), flip(z, x);
}
report(ans);
}
}
auto invert_vec = [&](const vector<int> &vec) {
bitset<MAXN> w;
for (int i : vec) w[i] = 1;
for (int i : vec) A[i] ^= w, A[i][i] = 0;
invert(vec);
};
while (n > k + 2) {
if (A[n].count() & 1) {
int x = A[n]._Find_first();
vector<int> vec;
for (int i = 1; vec.size() < k - 2; ++i)
if (i != x) vec.emplace_back(i);
vec.emplace_back(x), vec.emplace_back(n);
invert_vec(vec);
}
vector<int> P;
for (int i = 1; i <= n; ++i) if (A[n][i]) P.emplace_back(i);
for (int i = 0; i < P.size(); i += 2) {
int u = P[i], v = P[i + 1];
vector<int> vec;
for (int i = 1; vec.size() < k - 2; ++i)
if (i != u && i != v) vec.emplace_back(i);
vec.emplace_back(n);
vec.emplace_back(u);
invert_vec(vec);
vec.pop_back();
vec.emplace_back(v);
invert_vec(vec);
}
--n;
}
m = 0;
for (int i = 1; i <= n; ++i) m += A[i].count();
m >>= 1;
auto ins = [&](const vector<int> &vec) {
bitset<MAXN> w;
for (int i : vec) w[i] = 1;
for (int i : vec) A[i] ^= w, A[i][i] = 0;
int u = 1;
while (w[u]) ++u;
int v = u + 1;
while (w[v]) ++v;
op[u].flip(v);
};
if (m & 1) {
vector<int> vec(k);
iota(vec.begin(), vec.end(), 1);
ins(vec);
}
vector<int> O;
for (int i = 1; i <= n; ++i) if (A[i].count() & 1) O.emplace_back(i);
for (int i = 0; i < O.size(); i += 2) {
int u = O[i], v = O[i + 1];
vector<int> vec;
for (int j = 1; vec.size() < k - 1; ++j)
if (j != u && j != v) vec.emplace_back(j);
vec.emplace_back(u);
ins(vec);
vec.pop_back();
vec.emplace_back(v);
ins(vec);
}
vector<pair<int, int>> E;
for (int i = 2; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
if (A[i][j]) E.emplace_back(i, j);
auto cyc = [&](int a, int b, int c, int d) {
vector<int> vec;
for (int i = 1; i <= n; ++i)
if (i != a && i != b && i != c && i != d) vec.emplace_back(i);
auto add = [&](int x, int y) {
vec.emplace_back(x), vec.emplace_back(y);
ins(vec);
vec.pop_back(), vec.pop_back();
};
add(a, b), add(b, c), add(c, d), add(d, a);
};
for (int i = 0; i < E.size(); i += 2) {
auto [a, b] = E[i];
auto [c, d] = E[i + 1];
if (a == c) cyc(1, b, a, d);
else if (a == d) cyc(1, b, a, c);
else if (b == c) cyc(1, a, c, d);
else if (b == d) cyc(1, a, b, c);
else cyc(1, a, b, c), cyc(1, b, c, d);
}
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
if (op[i][j]) {
vector<int> vec;
for (int x = 1; x <= n; ++x)
if (x != i && x != j) vec.emplace_back(x);
invert(vec);
}
}

浙公网安备 33010602011771号