2.2 图论建模 & 图论杂项
\({\Large 约定}\):
- 用集合符号表示位运算符号,用 $ \oplus $ 表示异或,特别的,$ i \in S$ 表示二进制数 \(S\) 的第 \(i\) 位为 \(1\)
- 用 \(V\) 表示值域,\(\sum\) 表示字符集,\(\omega\) 表示
bitset的常数 \((\omega = 64)\)- 除去用 \(()/[]\) 表示开闭区间外,\([]\) 仅表示艾弗森约定,\(\{\}\) 仅表示集合,括号嵌套全用 \(()\)
- 字符串或序列角标为区间表示对应区间的子串
\(\textcolor{white}{\mathrm{pw:123456}}\)
#625. 【统一省选2021 A卷】矩阵游戏
\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 图论 差分约束 构造
先不考虑对 \(a_{i,j}\) 大小的限制,那么有一种简单构造:
- 令 \(a_{i,0}=0,a_{0,i}=0\)
- \(a_{i,j}=b_{i-1,j-1}-a_{i-1,j}-a_{i,j-1}-a_{i-1,j-1}\)
这个的正确性是显然的。但是不一定满足 \(a_{i,j}\) 的范围限制。考虑如何调整。
对于一个 \(x\),如果对一行的所有数字一次进行 $+x,-x,+x,-x\cdotp $ 的操作,那么 \(b\) 矩阵不会发生变化。对列操作也是同理。设 \(r_i\) 为在第 \(i\) 行上操作的 \(x\) 值,设 \(c_i\) 为在第 \(i\) 列上操作的 \(x\) 值。
那么现在问题就转化成了对 \(r_i\) 与 \(c_i\) 的限制了。根据 \(a_i\) 的限制可以列出下面的不等式:
在这里,\(a_{i,j}\) 都是确定的,那么将 \(a_{i,j}\) 移项得到:
发现这个比较像差分约束的形式,考虑要怎么做。
那么对于 \(c_i\) 和 \(d_j\),我们可以分别列出一个矩阵表示对应位置的 \(c_i\) 与 \(d_j\) 的正负性。
但是这样可能会出现 \(c_i\) 与 \(d_j\) 正负性相同的情况,因此可以将 \(c_i\) 与 \(d_j\) 稍微位移一下:
这样每个位置 \(c_i\) 与 \(d_j\) 的正负性就不一样了,也就可以转化成差分约束去做了。
以 \(c_i\) 为 \(+\),\(d_j\) 为 \(-\) 为例,上面的式子就变成了:
那么只需要建 \((d_j,c_i,10^6-a_{i,j}),(c_i,d_j,a_{i,j})\) 这样两条边即可。
当然还需要建一个虚拟源点向每个点连一条长度为 \(0\) 的有向边。
正确性证明可以看下面的文章。
用 vector 存图会更快。
Uoj 上要限制一下判断负环的次数才能过,不知道为啥。
Code
const int N = 1e6 + 10;
const int M = 1e3 + 10;
const int inf = 1e6;
int n, m;
int a[M][M], b[M][M];
ll dis[N];
bool vis[N];
int cnt[N];
std::vector<pii> ve[N];
int q[N], he, tl;
void init(int x) {
REP(i, 0, x + 1) ve[i].clear();
return;
}
bool spfa() {
he = 1, tl = 0;
q[++tl] = 0;
REP(i, 0, n + m + 2) dis[i] = 1e9, cnt[i] = vis[i] = 0;
dis[0] = 0;
vis[0] = 1;
while(he <= tl) {
int u = q[he];
he++;
vis[u] = 0;
cnt[u]++;
if (cnt[u] == n + m + 1 or cnt[u] == 30) return 0;//不知道什么神秘减枝
for(auto it:ve[u]) {
int v = it.fi, w = it.se;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!vis[v]) {
q[++tl] = v;
vis[v] = 1;
}
}
}
}
return 1;
}
void main() {
read(n, m);
REP(i, 1, n - 1)
REP(j, 1, m - 1) read(b[i][j]);
init(n + m + 2);
REP(i, 2, n) {
REP(j, 2, m) {
a[i][j] = b[i - 1][j - 1] - a[i][j - 1] - a[i - 1][j] - a[i - 1][j - 1];
}
}
REP(i, 1, n) {
REP(j, 1, m) {
int u = i, v = j + n;
if ((i + j) & 1) std::swap(u, v);
ve[v].pb({u, inf - a[i][j]});
ve[u].pb({v, a[i][j]});
}
}
REP(i, 1, n + m) ve[0].pb({i, 0});
if (!spfa()) return puts("NO"), void();
puts("YES");
REP(i, 1, n) {
REP(j, 1, m) {
int sum = (dis[i] - dis[j + n]);
if ((i + j) & 1) sum *= -1;
a[i][j] += sum;
std::cout << a[i][j] << ' ';
}
puts("");
}
return ;
}
2026.01.30

浙公网安备 33010602011771号