JEOI-R1:棋

「JEOI-R1棋

题目前言

巨大诈骗题

题面

题目描述

现在有一个 n×m 的棋盘,从上到下依次是 1n 行,从左到右依次是 1m 列,一个位于第 x 行第 y 列的位置被标记为 (x,y)。共有 c 个棋子,不重叠地摆放在棋盘的某些位置上。一个位于 (x,y) 的棋子可以走向 (x1,y1),(x1,y+1),(x+1,y1),(x+1,y+1)(如果这些位置存在且其上没有棋子)。

现有若干询问,每次询问给定 x1,y1,x2,y2,p,然后给定 p 个位置,表示一个子矩阵的左上角位置为 (x1,y1),右下角位置为 (x2,y2),问是否可以移动棋子(无次数限制)使得矩阵内有且仅有给定的 p 个位置上有棋子。询问之间相互独立。

为了减少程序时间复杂度的常数影响,建议使用更快的读入方式。

输入格式

第一行三个正整数 n,m,c,表示棋盘的列数、行数和已有的棋子数。

接下来 c 行,一行两个整数 ai,bi,表示有一个棋子位于 aibi 列处。

接下来一行一个正整数 q

接下来 q 组询问。每一组询问第一行是五个正整数 x1,y1,x2,y2,p,接下来 p 行每行两个正整数 ci,di,表示只希望矩阵内有棋子的位置。

输出格式

对于每个询问,如果可以移动棋子(无次数限制)使得矩阵内有且仅有给定的位置上有棋子,输出 YES,否则输出 NO

样例 #1

样例输入 #1

3 3 5
1 2
1 3
2 1
3 2
3 3
3
1 2 2 3 0
1 2 3 3 4
1 2
1 3
2 3
3 3
1 1 2 3 2
1 3
2 2

样例输出 #1

NO
YES
NO

@ 提示

【样例解释 #1】

解释以 0 代表空位,1 代表放置了棋子的位置。

初始状态:

011
100
011

对于第一个询问,可以证明 (1,2) 处的棋子无法移出 (1,2)(2,3) 的矩阵。

对于第二个询问,考虑把 (3,2) 处的棋子移到 (2,3),得:

011
101
001

满足询问要求。移动方式不唯一。

对于第三个询问,可以证明 (2,1) 处的棋子无法移出 (1,1)(2,3) 的矩阵。

【数据范围】

对于 25% 的数据,n,m,q10c20

对于另外 25% 的数据,保证 ai+bi0(mod2)ci+di0(mod2)

对于另外 25% 的数据,保证 nmc(x2x1+1)(y2y1+1)p

对于 100% 的数据,2n,m1051c,q105cnm1ain1bimp2×105。对于每个询问,1p(x2x1+1)(y2y1+1)x1cix2y1diy2

【提示与说明】

提供一种较快的读入一个 int 类型非负整数的方式。调用下文中的 read(),其作用是返回输入中的一个非负整数,同时读取其后的一个字符。

int read() {
  int x(0);
  char c(getchar());
  while (c < '0' || c > '9') c = getchar();
  while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
  return x;
}

题解

巨大诈骗题

首先看着移动方式:

棋子可以走向 (x1,y1),(x1,y+1),(x+1,y1),(x+1,y+1)

不难发现,更改后的x,y满足:x+yx+y(mod2),这启发我们分奇偶性讨论这个问题
以下称一类格为横纵坐标之和为奇的格子,零类格为横纵坐标之和为偶的格子

引理 :
一个棋子可以到达棋盘上与其同类的格子

由移动前后奇偶性不变可得必要性,现在来证明充分性。

本题的棋子移动数量不限,则可以以一种延迟撤回的思想,即由这个棋子往目标方向跳,若有撞上的则将撞上的移开,若撞上的那个棋子无路可走,则又移动撞上的那个棋子的周围棋子,如此类似递归步骤,可知这个棋子无论如何可以被移开。故充分性得证。

那么本题做法就比较明显了:先预处理出原矩阵(1,1,n,m)中零一类点个数分别记为Q0,Q1,将零一类棋子数量分别记为S0,S1。对于每一次输入的待求矩阵,统计出它总共的零一类点的个数为T0,T1,并求出需要移动棋子到达的位置(对应ci,di)中零一类位置有P0,P1个,则这个问题有解的充要条件为:

{P0S0P1S1

{S0P0Q0T0S1P1Q1T1

然后我们讨论对于这些变量的求法。首先若nm为偶数,那么Q0,Q1就都等于nm2,否则Q0=nm2,Q1=nmQ0(第一个出现的是(1,1),属于零类点。)

而对于T0,T1的计算方式类似于Q0,Q1,记l=x2x1+1,r=y2y1+1,若lr0(mod2)则平分,否则先将T0,T1全部赋值为lr2,然后根据(x1,y1)所属哪一类点,则将所对应的T加上1。至于S0,S1,P0,P1就没得说了,非常简单。

十年OI一场空,不开longlong见祖宗

#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
int n,m,t1,t0,p,q0,q1,s1,s0,a,b,c,d;
int read() {
  int x(0);
  char c(getchar());
  while (c < '0' || c > '9') c = getchar();
  while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
  return x;
}

signed main(){
	n=read(),m=read(),c=read();
	q1=n*m/2,q0=n*m-q1;
	for(int i=1;i<=c;i++){
		int x=read(),y=read();
		if((x+y)&1)s1++;
		else s0++; 
	}
	int t=read();
	while(t--){
		int x1=read(),y1=read(),x2=read(),y2=read();
		int l=x2-x1+1,r=y2-y1+1,p0=0,p1=0;
		if((l*r)&1){
			if((x1+y1)&1){
				t0=l*r/2;
				t1=l*r-t0;
			}
			else {
				t1=l*r/2;
				t0=l*r-t1;
			}
		}
		else {
			t1=l*r/2;
			t0=l*r/2;
		}
		int q=read();
		for(int i=1;i<=q;i++){
			int x=read(),y=read();
			if((x+y)&1)p1++;
			else p0++;
		} 
		if(p0<=s0&&p1<=s1&&(s0+t0)<=(q0+p0)&&(s1+t1)<=(q1+p1))puts("YES");
		else puts("NO");
	}
}
posted @   spdarkle  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示