[qoj4884]Battleship: New Rules

\(m=n+1\),注意区分"格子"和"格点"

由于不能有公共格点,不妨从此角度分析——

  • 格点有\(m\times m\)个,每次覆盖其中\(2\times a\)的矩形(其中\(a\ge 2\)
  • 覆盖格子与格点总数的关系为\(s\rightarrow 2(s+k)\),即最大化两者等价
  • \(2\times 2\)的未覆盖格子等价于非边界未覆盖格点

\(m\)为偶数时,显然可以覆盖所有格点,即无解

结论:\(m\)为奇数时,恰存在一个格点未被覆盖

显然至少存在一个格点未被覆盖,下面考虑构造:

  • \(m=5\)时,用\(4\)\(2\times 3\)的矩形"旋转"覆盖即可
  • 否则,用\(2\times m/(m-2)\)的矩形覆盖后,转换为\(m-2\)的子问题即可

在此基础上,考虑判定该格点是否在(格点)矩形\(([x_{1},x_{2}],[y_{1},y_{2}])\)中:

由于该格点唯一,仅需求出矩形中被覆盖格点数的奇偶性

除了形如\(([x_{1}-1,x_{1}],*)\)的(格点)矩形,每次覆盖与该矩形的公共格点数均为偶数

对于这类矩形,从格子的角度,即覆盖\(([x_{1},x_{2}],[y_{1},y_{2}])\)外一圈的格子

这些格子中,可能有覆盖方向不同的格子,但这样的公共格点数为\(2\),并不影响

换言之,可以认为仅有这些格子,并覆盖了其所有顶点,统计公共格点数即可

在此基础上,考虑分治,每次将较长的一维分为两半,并以此判定是否在某侧即可

将其中重复的询问合并,归纳当前矩形外层均已查询,每次仅需对分治处两侧查询

总查询次数为\(2(m+\frac{m}{2}+\frac{m}{2}+...)\approx 6m\),每层随机查询一侧即可将期望除以\(2\)

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int t,n,vis[N][N];
void write(int x,int y){
	printf("! %d %d\n",x,y);
	fflush(stdout);
	scanf("%*d");
}
int query(int x,int y){
	if ((x<1)||(y<1)||(x>=n)||(y>=n))return 0;
	if (vis[x][y]<0){
		printf("? %d %d\n",x,y);
		fflush(stdout);
		scanf("%d",&vis[x][y]);
	}
	return vis[x][y];
}
int Query(int x,int y){
	return (query(x,y)|query(x,y+1)|query(x+1,y)|query(x+1,y+1));
}
bool check(int x1,int x2,int y1,int y2){
	int s=(x2-x1+1&1)*(y2-y1+1&1);
	for(int i=x1+1;i<x2;i++){
		s^=(query(i,y1-1)|query(i-1,y1-1));
		s^=(query(i,y2)|query(i-1,y2));
	}
	for(int i=y1+1;i<y2;i++){
		s^=(query(x1-1,i)|query(x1-1,i-1));
		s^=(query(x2,i)|query(x2,i-1));
	}
	if ((x1==x2)&&(y1==y2))s^=Query(x1-1,y1-1);
	else{
		if ((x1==x2)||(y1==y2))s^=(Query(x1-1,y1-1)^Query(x2-1,y2-1));
		else{
			s^=(query(x1-1,y1-1)|query(x1-1,y1)|query(x1,y1-1));
			s^=(query(x1-1,y2)|query(x1-1,y2-1)|query(x1,y2));
			s^=(query(x2,y1-1)|query(x2,y1)|query(x2-1,y1-1));
			s^=(query(x2,y2)|query(x2,y2-1)|query(x2-1,y2));
		}
	}
	return s;
}
void solve(int x1,int x2,int y1,int y2){
	if ((x1==x2)&&(y1==y2)){
		if ((x1==1)||(x1==n)||(y1==1)||(y1==n))write(-1,-1);
		else write(x1-1,y1-1);
		return;
	}
	if (x2-x1>y2-y1){
		int mid=(x1+x2>>1);
		if (check(x1,mid,y1,y2))solve(x1,mid,y1,y2);
		else solve(mid+1,x2,y1,y2);
	}
	else{
		int mid=(y1+y2>>1);
		if (check(x1,x2,y1,mid))solve(x1,x2,y1,mid);
		else solve(x1,x2,mid+1,y2);
	}
}
int main(){
	scanf("%d",&t);
	while (t--){
		scanf("%d",&n);
		n++;
		memset(vis,-1,sizeof(vis));
		if ((n<5)||(n&1^1))write(-1,-1);
		else solve(1,n,1,n);
	}
	return 0;
}
posted @ 2023-02-13 22:47  PYWBKTDA  阅读(61)  评论(0编辑  收藏  举报