1 条题解
-
0
自动搬运
来自洛谷,原作者为

modfish_
你指尖跃动的电光,事静电罢(恼搬运于
2025-08-24 22:52:20,当前版本为作者最后更新于2025-07-08 18:57:18,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
以下是一大坨。
Part 1
首先可以令 上的边权全部减去 上对应边权。然后目标变成了把 上的权全部变成 。
一个显然的想法是把 的邻边依次变为 ,当然这是做不到的。不过可以从 做到 。
具体地,设当前枚举的点为 ,取出 放着作为工具点,对于 ,显然可以做一次 操作把边 变成 。
最后剩下 个点,怎么办?
给出第一个操作组:选定四个点 ,满足 为偶数,将 的权值全部转移至 ,不影响其余任何边。
操作:记初始 的边权为 ,进行 。
可以画图发现这是对的。
所以如果 ()的边权是偶数,那么可以轻易地把它转移走。如果有奇数怎么办?
如果只看奇偶性的话,进行一次 实际上就是把六条边的边权的奇偶性都改变一次。对应到这个情境中,就是可以选择三个点,把 的边权奇偶性改变。
也就是:有 个取值 的数,每次把其中 个反转,要求全部变成 。
无论如何都可以做到。下面不带编号地给出做法:
- 个 :$\{1,1,1,1\}\rightarrow\{1,0,0,0\}\rightarrow\{0,1,1,0\}\rightarrow\{1,0,1,1\}\rightarrow\{0,0,0,0\}$。
- 个 :。
- 个 :$\{1,1,0,0\}\rightarrow\{1,0,1,1\}\rightarrow\{0,0,0,0\}$。
- 个 :$\{1,0,0,0\}\rightarrow\{0,1,1,0\}\rightarrow\{1,0,1,1\}\rightarrow\{0,0,0,0\}$。
- 个 :不用操作。
所以一定可以把 的边权都变成偶数,最后再全部转移走即可。
Part 2
现在问题变成了一个 个点的完全图,首先可以手玩一下 何时有解。
首先边权和必定要为 ,因为操作不改变边权和。
其次, 时只有 种本质不同的操作,即 时的操作,可以记操作的总权值为 。则有方程:
$$\begin{cases} x_1+x_2-x_3-x_4=-w_{1,2}\\ x_1-x_2+x_3-x_4=-w_{1,3}\\ x_1-x_2-x_3+x_4=-w_{1,4}\\ -x_1+x_2+x_3-x_4=-w_{2,3}\\ -x_1+x_2-x_3+x_4=-w_{2,4}\\ -x_1-x_2+x_3+x_4=-w_{3,4}\\ \end{cases}$$所以可以得出结论:
这是另一个必要条件。
然后,进行加减消元,可以得出:
所以右边三项都得是偶数,这是第三个必要条件。
都满足后,令 ,就可以得出 ,直接做就行。
Part 3
你要是认为这就做完了就大错特错了(这么认为的我就是大奶龙)。
以上只是 的情况,而非剩下 个点的情况。比方说, 时,可以做到 做不到的操作。
我们已经知道了第一个操作组——对偶数边权的转移。所以给出结论:当且仅当剩下的 条边权奇偶性相同,且和为 时有解。
必要性证明略。充分性证明:如果权值都是奇数就先做一次 变成偶数。然后通过和 相邻的 条边,把所有边权转移到同一条边上即可。
Part 4
然而还没有完。 时不能接受奇数边权,然而 时是可以的。
给出第二个操作组:令 ,将 的边权减 , 的边权加 ,不影响其余任何边。
操作:先操作 ,可以发现这样 的边权分别增加 ;然后操作 ,这样 的边权分别减少 ,总体来看只有 边权改变。最后操作 ,即可将 加 , 减 ,并将其余边权恢复。
画个图更容易理解。
所以,先判定边权和为 ,然后直接找出边权为奇数的边,把它们的边权转移 到 ,最后将 的边权(可以证明一定是个偶数)转移到某条边上,之后和 的一样。
总结
把 的边权减去 ,先把除 之间的边以外的边全部变成 ,然后分为 、 和 分类讨论即可。
分析以下,除了一开始把其他边变成 花费了 次操作外,剩余都是常数级别的,完全可以通过。实测本题数据每个点不超过 次。
代码
#include <bits/stdc++.h> using namespace std; const int maxn = 105; int G[maxn][maxn], H[maxn][maxn]; int ans[maxn * maxn * maxn][5], cnt = 0; void operateG(int a, int b, int c, int d, int w){ cnt ++, ans[cnt][0] = a, ans[cnt][1] = b, ans[cnt][2] = c, ans[cnt][3] = d, ans[cnt][4] = w; G[a][b] += w, G[b][a] += w, G[a][c] += w, G[c][a] += w, G[a][d] += w, G[d][a] += w; G[b][c] -= w, G[c][b] -= w, G[c][d] -= w, G[d][c] -= w, G[b][d] -= w, G[d][b] -= w; } void solve_even(){ operateG(3, 1, 2, 4, G[1][2] / 2), operateG(4, 1, 2, 3, G[1][2]); operateG(4, 1, 2, 3, G[1][3] / 2), operateG(2, 1, 3, 4, G[1][3]); operateG(2, 1, 3, 4, G[1][4] / 2), operateG(3, 1, 2, 4, G[1][4]); operateG(1, 2, 3, 5, G[2][3] / 2), operateG(5, 1, 2, 3, G[2][3]); operateG(1, 3, 4, 5, G[3][4] / 2), operateG(5, 1, 3, 4, G[3][4]); operateG(2, 1, 4, 5, G[1][5] / 2), operateG(4, 1, 2, 5, G[1][5]); } void swap_one(int a, int b, int c, int d){ operateG(5, a, b, c, 1), operateG(d, b, c, 5, 1); operateG(6, a, b, c, -1), operateG(d, b, c, 6, -1); operateG(6, 5, a, d, 1); } int main(){ int n; scanf("%d", &n); for(int i = 1; i <= n; i ++){ for(int j = i + 1; j <= n; j ++){ scanf("%d", &G[i][j]); G[j][i] = G[i][j]; } } for(int i = 1; i <= n; i ++){ for(int j = i + 1; j <= n; j ++){ scanf("%d", &H[i][j]); H[j][i] = H[i][j]; G[i][j] -= H[i][j], G[j][i] -= H[j][i]; } } for(int x = n; x >= 5; x --){ for(int i = n; i >= 5; i --){ if(i == x) continue; operateG(x, 1, 2, i, -G[x][i]); } int a = G[x][1] & 1, b = G[x][2] & 1, c = G[x][3] & 1, d = G[x][4] & 1; if(a + b + c + d == 4){ operateG(x, 1, 2, 3, 1); operateG(x, 1, 2, 4, 1); operateG(x, 1, 3, 4, 1); operateG(x, 2, 3, 4, 1); }else if(a + b + c + d == 3){ if(!a) operateG(x, 2, 3, 4, 1); else if(!b) operateG(x, 1, 3, 4, 1); else if(!c) operateG(x, 1, 2, 4, 1); else operateG(x, 1, 2, 3, 1); }else if(a + b + c + d == 2){ if(a && b) operateG(x, 2, 3, 4, 1), operateG(x, 1, 3, 4, 1); else if(a && c) operateG(x, 2, 3, 4, 1), operateG(x, 1, 2, 4, 1); else if(a && d) operateG(x, 2, 3, 4, 1), operateG(x, 1, 2, 3, 1); else if(b && c) operateG(x, 1, 2, 4, 1), operateG(x, 1, 3, 4, 1); else if(b && d) operateG(x, 1, 2, 3, 1), operateG(x, 1, 3, 4, 1); else operateG(x, 1, 2, 3, 1), operateG(x, 1, 2, 4, 1); }else if(a + b + c + d == 1){ if(a) operateG(x, 1, 2, 3, 1), operateG(x, 1, 2, 4, 1), operateG(x, 1, 3, 4, 1); else if(b) operateG(x, 1, 2, 3, 1), operateG(x, 1, 2, 4, 1), operateG(x, 2, 3, 4, 1); else if(c) operateG(x, 1, 2, 3, 1), operateG(x, 1, 3, 4, 1), operateG(x, 2, 3, 4, 1); else operateG(x, 1, 2, 4, 1), operateG(x, 1, 3, 4, 1), operateG(x, 2, 3, 4, 1); } a = -G[x][1] / 2, b = -G[x][2] / 2, c = -G[x][3] / 2, d = -G[x][4] / 2; operateG(x, 1, 2, 3, a), operateG(1, x, 2, 3, a); operateG(x, 2, 1, 3, b), operateG(2, x, 1, 3, b); operateG(x, 3, 1, 2, c), operateG(3, x, 1, 2, c); operateG(x, 4, 1, 2, d), operateG(4, x, 1, 2, d); } int w12 = -G[1][2], w13 = -G[1][3], w14 = -G[1][4], w23 = -G[2][3], w24 = -G[2][4], w34 = -G[3][4]; if(n == 4){ if(w12 + w34 != 0 || w13 + w24 != 0 || w14 + w23 != 0){ printf("-1\n"); return 0; } if(((w12 + w13) & 1) || ((w12 + w23) & 1) || ((w13 + w23) & 1)){ printf("-1\n"); return 0; } int x1 = (w12 + w13) / 2, x2 = (w12 + w23) / 2, x3 = (w13 + w23) / 2; operateG(1, 2, 3, 4, x1), operateG(2, 1, 3, 4, x2), operateG(3, 1, 2, 4, x3); }else if(n == 5){ int sum = (w12 & 1) + (w13 & 1) + (w14 & 1) + (w23 & 1) + (w24 & 1) + (w34 & 1); if((sum != 0 && sum != 6) || (w12 + w13 + w14 + w23 + w24 + w34 != 0)){ printf("-1\n"); return 0; } if(sum == 6) operateG(1, 2, 3, 4, 1); solve_even(); }else{ if(w12 + w13 + w14 + w23 + w24 + w34 != 0){ printf("-1\n"); return 0; } if(G[1][2] & 1) swap_one(1, 3, 4, 2); if(G[1][3] & 1) swap_one(1, 2, 4, 3); if(G[1][4] & 1) swap_one(1, 2, 3, 4); if(G[2][3] & 1) swap_one(2, 1, 4, 3); if(G[2][4] & 1) swap_one(2, 1, 3, 4); if(G[3][4] & 1) swap_one(3, 1, 2, 4); operateG(1, 2, 5, 6, G[5][6] / 2), operateG(2, 1, 5, 6, G[5][6]); solve_even(); } printf("%d\n", cnt); for(int i = 1; i <= cnt; i ++) printf("%d %d %d %d %d\n", ans[i][0], ans[i][1], ans[i][2], ans[i][3], ans[i][4]); return 0; }
- 1
信息
- ID
- 9357
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者