【叭叭】CF1658E - Gojou and Matrix Game

入选原因

Warning
“叭叭” 专栏的所有思路不保证绝对正确,但是确实能够帮助AC,也确实值得记录。大概保真的思路参见“题解”专栏中带【题解】前缀的文章。

  1. 它挺好玩的
  2. 方法挺经典的

题意

Marin 和 Gojou 在 \(n\times n\) 的棋盘里选格子。每次选的格子与上一个人选的曼哈顿距离 \(>k\) .
可以得到选的格子上相应的分数(是 \(1--n^2\) 的排列),可以重复选。每个人可以选 \(10^{100}\) 次,最终得分和较大的获胜。
问 Marin 先选 每个 格子的时候,最终的胜负情况。

\(3\le n\le 2000,\, 1\le k\le n−2\).

思考过程

首先大家都可以玩很多轮,所以考虑游戏从什么时候开始就可以分出胜负了 (狼人什么时候赢
稍微玩一下,发现当有一个人可以占领 val 最大的格子,游戏就会结束。
(当然也有别的情况,但是先不管。)
X 占领了格子 (i,j),意味着另一个人 Y 上一步选的格子 \((i',j')\), 满足\(\text{dis}((i,j),(i',j'))>k\)

记 val=\(n^2\) 的点为 P,和 P 距离 \(\le k\) 的点组成集合 \(S^1\).
那如果 Marin 一开始的起点在 \(S^1\) 之外,Gojou 就可以直接选 P,同时因为 G 选了 P,M 在下一步不能选 P,而只能选 val<\(n^2\) 的数。G 赢麻了!
\(S^1\) 之外的点都是 G 赢,不用考虑了。接下来处理 \(S^1\) 里的数。

\(T_{某个数}\) 为与这个数距离不超过 \(k\) 的点的集合。
递归地想,取集合 \(S^i\) 里最大的数,令 \(S^{i+1}=S^i \bigcap T_{这个数}\),每次是不是可以根据 \(k\le n-2\) 至少确定一半的状态?

怎么实现捏( ̄▽ ̄)*

暴力模拟。会获得 \(\text{TLE on 6}\) 的好成绩。原因是有可能删到最后,\(S\) 里的数彼此之间距离都 \(\le k\),退化到一个个删。
(但是怎么看,每个数都只被删一次,复杂度应该是 \(O(n^2\log n)\) 级别的才对呀,不是很懂)

想想。手段确实还可以再聪明很多。

在一个游戏中, 玩法 确定了,各个 局面 之间的关系也会确定。
在一个游戏中, 决策 确定了,各个 状态 之间的关系也会确定。

一次决策中,我们只关心它选了什么,会导致是谁赢。
\(f[i][j]\) 表示 M 先选了 \((i,j)\),她能不能赢。 \(f[\text{pos of } n^2]=1\).

记 f[i][j]=1 的点组成集合S。
那么由第二段,集合 S 里的数两两距离 \(\le k\)

依然按 val 从大到小确定。在 \(S^1\) 外面的点会被第一层限制(距离 val=\(n^2\) 的点 \(\le k\))筛掉。第二个加入 S 的数便是集合 \(S^1\) 中次大的数,它会成为之后加入的数的新的限制。
这个操作其实是在选交集!

最后处理距离。
常用技巧:

\[|i-i'|+|j-j'|\le k \\ \Leftrightarrow \max(|i-i'|+|j-j'|)\le k \\ \Leftrightarrow \max(\max(i-i',i'-i),\max(j-j',j'-j))\le k \\ \Leftrightarrow \max(i+j-i'-j',\, -i-j+i'+j', \, i-j-i+j', \, -i+j+i'-j')\le k \\ \Leftrightarrow \max(|(i+j)-(i'+j')|, \, |(i-j)-(i'-j')|)\le k \]

(i+j), (i-j) 的 min 和 max 最值是可以用来表征整个集合的。维护这四个值即可。

CF的题解:

\(dp[i][j]=1\) if \(\forall\) (i′,j′) such that \(|i−i′|+|j−j′|>k\), we have \(dp[i′][j′]=0\).
Note that by taking the contrapositive, this is equivalent to \(\forall\) (i′,j′) such that \(dp[i′][j′]=1\), we have \(|i−i′|+|j−j′|\le k\).

逆否命题看起来很高级,是我逻辑没学好😔

Code

#include<bits/stdc++.h>
using namespace std;
const int N=2005;
int n,k,w;
struct node {
	int x,y,v;
	node(){}
	node(int _x,int _y,int _v){
		x=_x, y=_y, v=_v;
	}
};
#define pr pair<int,int>
#define mp make_pair
#define px first
#define py second
pr p[N*N];
bool f[N][N];

int main() {
	scanf("%d%d",&n,&k);
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=n; j++) {
			int v;
			scanf("%d",&v);
			p[v]=mp(i,j);
		}
	}
	node t=node(p[n*n].px,p[n*n].py,n*n);
	f[t.x][t.y]=1;
	int m[2][2]={{t.x-t.y,t.x-t.y},{t.x+t.y,t.x+t.y}};//minus/plus, min/max
	for(int i=n*n-1;i;i--){
		int m0=p[i].px-p[i].py, m1=p[i].px+p[i].py;
		if(max({abs(m[0][0]-m0),abs(m[0][1]-m0),abs(m[1][0]-m1),abs(m[1][1]-m1)})>k) continue;
		m[0][0]=min(m[0][0],m0), m[0][1]=max(m[0][1],m0);
		m[1][0]=min(m[1][0],m1), m[1][1]=max(m[1][1],m1);
		t=node(p[i].px,p[i].py,i);
		f[t.x][t.y]=1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) printf("%c",f[i][j]?'M':'G');
		printf("\n");
	}
	return 0;
}

Ура!

posted @ 2022-11-13 12:13  Searshkiu  阅读(19)  评论(0编辑  收藏  举报