Codeforces Round #779 (Div. 2) E. Gojou and Matrix Game

23 篇文章 2 订阅
18 篇文章 0 订阅

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 xx1+yy1>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 xx1+yy1k
那么即后手没有机会赢,先手必胜,反之先手必败。

那么实际上式子也就可用写成:
m a x ( ∣ x − x 1 ∣ + ∣ y − y 1 ∣ ) ≤ k max(|x-x_1|+|y-y_1|)\leq k max(xx1+yy1)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)|) xx1+yy1=max((x+y)(x1+y1),(xy)(x1y1))

这里粗略的证明一下上述公式, ∣ x − x 1 ∣ + ∣ y − y 1 ∣ |x-x_1|+|y-y_1| xx1+yy1 把绝对值拆开只可能产生四个式子:
( 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) (xy)(x1y1)
( x 1 − y 1 ) − ( x − y ) (x_1-y_1)-(x-y) (x1y1)(xy)

这里设 ① = ∣ x − x 1 ∣ ①=|x-x_1| =xx1 ② = ∣ y − y 1 ∣ ②=|y-y_1| =yy1 ∣ x − x 1 ∣ + ∣ y − y 1 ∣ |x-x_1|+|y-y_1| xx1+yy1 正确的答案应该是 ① + ② ①+② + ,而在上述把绝对值拆开的四个等式中倘若不是正确的值,则只可能是 ① ① ② ② 取了负号,那么答案一定会变小,所以正确的值一定是上述四个式子中最大的一个。那么再将上述四个式子用绝对值两两合并写一下就是:

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),(xy)(x1y1))
证毕

因此我们最终可以得到:
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(xx1+yy1)kmax(max((x+y)(x1+y1),(xy)(x1y1)))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((xy)(x1y1)) ,最后再取 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((xy)(x1y1))=max((xy)min(x1y1),(xy)max(x1y1))

这样我们只要维护 ( x 1 + y 1 ) (x_1+y_1) (x1+y1) ( x 1 − y 1 ) (x_1-y_1) (x1y1) 分别的最大最小值就可以知道上述式子是否成立,若成立则 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("");
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值