E. Gojou and Matrix Game
这里我们定义一次(先手,后手)为一轮游戏,因为进行
1
0
100
10^{100}
10100 次,所以可以进行整数轮游戏。
首先我们可以发现如果先手站在最大值上直接就赢了,因为后手无论走哪个点,先手又可以回来取最大点,所以每一轮先手都可以比后手优,所以必胜。
然后我们考虑先手初始在非最大值的情况,我们可以发现,如果先手站在非最大值上,假设这个值是 v v v ,那么后手下一步必须走到 > v >v >v 的点,为何呢,如果后手不这么做,选了一个 < v <v <v 的点,先手下一次又可以选回 v v v ,本身我后手第一轮我就亏了,然后还回到了与上一轮一样的情况(先手选了 v v v ),说白了我白亏一轮,所以后手不可能白亏,所以他必定会选 > v >v >v 的点。
那么我们发现,我们可以按 v v v 值倒序遍历,递推每个点的答案。这里我们设 f [ x ] [ y ] f[x][y] f[x][y] 表示初始站在 ( x , y ) (x,y) (x,y) 点的胜败态, f [ x ] [ y ] = 0 f[x][y]=0 f[x][y]=0 为败, f [ x ] [ y ] = 1 f[x][y]=1 f[x][y]=1 为胜。
那么我们已知 v [ x ] [ y ] v[x][y] v[x][y] 为 m a x max max 时, f [ x ] [ y ] = 1 f[x][y]=1 f[x][y]=1
假设当前遍历到
(
x
,
y
)
(x,y)
(x,y) ,那么后手只有到达点
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 满足
v
[
x
1
]
[
y
1
]
>
v
[
x
]
[
y
]
v[x_1][y_1]>v[x][y]
v[x1][y1]>v[x][y] 且
f
[
x
1
]
[
y
1
]
=
1
f[x_1][y_1]=1
f[x1][y1]=1 才能赢,因为我们是倒序遍历,所以比
v
[
x
]
[
y
]
v[x][y]
v[x][y] 值大的点,且是胜利态的点
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 我们是已经得到了的,那么
(
x
,
y
)
(x,y)
(x,y) 能走到
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 的条件是 :
∣
x
−
x
1
∣
+
∣
y
−
y
1
∣
>
k
|x-x_1|+|y-y_1|>k
∣x−x1∣+∣y−y1∣>k
那么只要存在一个满足上述条件的点
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 后手即可获胜,这里我们可以反过来求,如果所有
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 都满足:
∣
x
−
x
1
∣
+
∣
y
−
y
1
∣
≤
k
|x-x_1|+|y-y_1|\leq k
∣x−x1∣+∣y−y1∣≤k
那么即后手没有机会赢,先手必胜,反之先手必败。
那么实际上式子也就可用写成:
m
a
x
(
∣
x
−
x
1
∣
+
∣
y
−
y
1
∣
)
≤
k
max(|x-x_1|+|y-y_1|)\leq k
max(∣x−x1∣+∣y−y1∣)≤k
那么我们将式子做一个转换:
∣
x
−
x
1
∣
+
∣
y
−
y
1
∣
=
m
a
x
(
∣
(
x
+
y
)
−
(
x
1
+
y
1
)
∣
,
∣
(
x
−
y
)
−
(
x
1
−
y
1
)
∣
)
|x-x_1|+|y-y_1|=max(|(x+y)-(x_1+y_1)|,|(x-y)-(x_1-y_1)|)
∣x−x1∣+∣y−y1∣=max(∣(x+y)−(x1+y1)∣,∣(x−y)−(x1−y1)∣)
这里粗略的证明一下上述公式,
∣
x
−
x
1
∣
+
∣
y
−
y
1
∣
|x-x_1|+|y-y_1|
∣x−x1∣+∣y−y1∣ 把绝对值拆开只可能产生四个式子:
(
x
+
y
)
−
(
x
1
+
y
1
)
(x+y)-(x_1+y_1)
(x+y)−(x1+y1)
(
x
1
+
y
1
)
−
(
x
+
y
)
(x_1+y_1)-(x+y)
(x1+y1)−(x+y)
(
x
−
y
)
−
(
x
1
−
y
1
)
(x-y)-(x_1-y_1)
(x−y)−(x1−y1)
(
x
1
−
y
1
)
−
(
x
−
y
)
(x_1-y_1)-(x-y)
(x1−y1)−(x−y)
这里设 ① = ∣ x − x 1 ∣ ①=|x-x_1| ①=∣x−x1∣ , ② = ∣ y − y 1 ∣ ②=|y-y_1| ②=∣y−y1∣ , ∣ x − x 1 ∣ + ∣ y − y 1 ∣ |x-x_1|+|y-y_1| ∣x−x1∣+∣y−y1∣ 正确的答案应该是 ① + ② ①+② ①+② ,而在上述把绝对值拆开的四个等式中倘若不是正确的值,则只可能是 ① ① ① 或 ② ② ② 取了负号,那么答案一定会变小,所以正确的值一定是上述四个式子中最大的一个。那么再将上述四个式子用绝对值两两合并写一下就是:
m
a
x
(
∣
(
x
+
y
)
−
(
x
1
+
y
1
)
∣
,
∣
(
x
−
y
)
−
(
x
1
−
y
1
)
∣
)
max(|(x+y)-(x_1+y_1)|,|(x-y)-(x_1-y_1)|)
max(∣(x+y)−(x1+y1)∣,∣(x−y)−(x1−y1)∣)
证毕
因此我们最终可以得到:
m
a
x
(
∣
x
−
x
1
∣
+
∣
y
−
y
1
∣
)
≤
k
⟺
m
a
x
(
m
a
x
(
∣
(
x
+
y
)
−
(
x
1
+
y
1
)
∣
,
∣
(
x
−
y
)
−
(
x
1
−
y
1
)
∣
)
)
≤
k
max(|x-x_1|+|y-y_1|)\leq k \iff max(max(|(x+y)-(x_1+y_1)|,|(x-y)-(x_1-y_1)|)) \leq k
max(∣x−x1∣+∣y−y1∣)≤k⟺max(max(∣(x+y)−(x1+y1)∣,∣(x−y)−(x1−y1)∣))≤k
因为两层都是 m a x max max ,所以我们只要对于所有 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 分别求 m a x ( ∣ ( x + y ) − ( x 1 + y 1 ) ) max(|(x+y)-(x_1+y_1)) max(∣(x+y)−(x1+y1)) 和 m a x ( ∣ ( x − y ) − ( x 1 − y 1 ) ∣ ) max(|(x-y)-(x_1-y_1)|) max(∣(x−y)−(x1−y1)∣) ,最后再取 m a x max max 即可。
那么我们其实就有:
m
a
x
(
∣
(
x
+
y
)
−
(
x
1
+
y
1
)
)
=
m
a
x
(
∣
(
x
+
y
)
−
m
i
n
(
x
1
+
y
1
)
∣
,
∣
(
x
+
y
)
−
m
a
x
(
x
1
+
y
1
)
∣
)
max(|(x+y)-(x_1+y_1))=max(|(x+y)-min(x_1+y_1)|, |(x+y)-max(x_1+y_1)|)
max(∣(x+y)−(x1+y1))=max(∣(x+y)−min(x1+y1)∣,∣(x+y)−max(x1+y1)∣)
m
a
x
(
∣
(
x
−
y
)
−
(
x
1
−
y
1
)
∣
)
=
m
a
x
(
∣
(
x
−
y
)
−
m
i
n
(
x
1
−
y
1
)
∣
,
∣
(
x
−
y
)
−
m
a
x
(
x
1
−
y
1
)
∣
)
max(|(x-y)-(x_1-y_1)|)=max(|(x-y)-min(x_1-y_1)|, |(x-y)-max(x_1-y_1)|)
max(∣(x−y)−(x1−y1)∣)=max(∣(x−y)−min(x1−y1)∣,∣(x−y)−max(x1−y1)∣)
这样我们只要维护 ( x 1 + y 1 ) (x_1+y_1) (x1+y1) 和 ( x 1 − y 1 ) (x_1-y_1) (x1−y1) 分别的最大最小值就可以知道上述式子是否成立,若成立则 f [ x ] [ y ] = 0 f[x][y]=0 f[x][y]=0 ,否则 f [ x ] [ y ] = 1 f[x][y]=1 f[x][y]=1 。若 f [ x ] [ y ] = 1 f[x][y]=1 f[x][y]=1 则需要更新最小和最大值。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
struct node {
int x;
int y;
int v;
bool operator < (const node &t) {
return v > t.v;
}
};
int n, k;
bool f[N][N];
vector<node> a;
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
int v; scanf("%d", &v);
a.push_back({i, j, v});
}
}
sort(a.begin(), a.end());
f[a[0].x][a[0].y] = 1;
int mn_xaddy, mn_xsuby, mx_xaddy, mx_xsuby;
mn_xaddy = mx_xaddy = a[0].x + a[0].y;
mn_xsuby = mx_xsuby = a[0].x - a[0].y;
for (int i = 1; i < n * n; ++i) {
int x = a[i].x, y = a[i].y;
// if (a[i].v == 10) {
// cout << max({abs(x+y-mn_xaddy), abs(x+y-mx_xaddy), abs(x-y-mn_xsuby), abs(x-y-mx_xsuby)}) << endl;
// }
if (max({abs(x+y-mn_xaddy), abs(x+y-mx_xaddy), abs(x-y-mn_xsuby), abs(x-y-mx_xsuby)}) <= k) {
f[x][y] = 1;
mn_xaddy = min(mn_xaddy, x+y);
mx_xaddy = max(mx_xaddy, x+y);
mn_xsuby = min(mn_xsuby, x-y);
mx_xsuby = max(mx_xsuby, x-y);
}
else {
f[x][y] = 0;
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
printf("%c", f[i][j] == 1 ? 'M' : 'G');
}
puts("");
}
}