「JEOI-R1棋
题目前言
巨大诈骗题
题面
题目描述
现在有一个 \(n\times m\) 的棋盘,从上到下依次是 \(1\sim n\) 行,从左到右依次是 \(1\sim m\) 列,一个位于第 \(x\) 行第 \(y\) 列的位置被标记为 \((x,y)\)。共有 \(c\) 个棋子,不重叠地摆放在棋盘的某些位置上。一个位于 \((x,y)\) 的棋子可以走向 \((x-1,y-1),(x-1,y+1),(x+1,y-1),(x+1,y+1)\)(如果这些位置存在且其上没有棋子)。
现有若干询问,每次询问给定 \(x_1,y_1,x_2,y_2,p\),然后给定 \(p\) 个位置,表示一个子矩阵的左上角位置为 \((x_1,y_1)\),右下角位置为 \((x_2,y_2)\),问是否可以移动棋子(无次数限制)使得矩阵内有且仅有给定的 \(p\) 个位置上有棋子。询问之间相互独立。
为了减少程序时间复杂度的常数影响,建议使用更快的读入方式。
输入格式
第一行三个正整数 \(n,m,c\),表示棋盘的列数、行数和已有的棋子数。
接下来 \(c\) 行,一行两个整数 \(a_i,b_i\),表示有一个棋子位于 \(a_i\) 行 \(b_i\) 列处。
接下来一行一个正整数 \(q\)。
接下来 \(q\) 组询问。每一组询问第一行是五个正整数 \(x_1,y_1,x_2,y_2,p\),接下来 \(p\) 行每行两个正整数 \(c_i,d_i\),表示只希望矩阵内有棋子的位置。
输出格式
对于每个询问,如果可以移动棋子(无次数限制)使得矩阵内有且仅有给定的位置上有棋子,输出 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,q\le 10\),\(c\le 20\)。
对于另外 \(25\%\) 的数据,保证 \(a_i+b_i\equiv 0 \pmod 2\),\(c_i+d_i\equiv 0 \pmod 2\)。
对于另外 \(25\%\) 的数据,保证 \(n\cdot m-c\le(x_2-x_1+1)\cdot (y_2-y_1+1)-p\)。
对于 \(100\%\) 的数据,\(2\le n,m\le 10^5\),\(1\le c,q\le 10^5\),\(c\le n\cdot m\),\(1\le a_i\le n\),\(1\le b_i\le m\),\(\sum p\le 2\times 10^5\)。对于每个询问,\(1\le p\le (x_2-x_1+1)\cdot (y_2-y_1+1)\),\(x_1\le c_i\le x_2\),\(y_1\le d_i\le y_2\)。
【提示与说明】
提供一种较快的读入一个 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;
}
题解
巨大诈骗题
首先看着移动方式:
棋子可以走向 \((x-1,y-1),(x-1,y+1),(x+1,y-1),(x+1,y+1)\)
不难发现,更改后的\(x',y'\)满足:\(x'+y'\equiv x+y (\bmod 2)\),这启发我们分奇偶性讨论这个问题
以下称一类格为横纵坐标之和为奇的格子,零类格为横纵坐标之和为偶的格子
引理 :
一个棋子可以到达棋盘上与其同类的格子
由移动前后奇偶性不变可得必要性,现在来证明充分性。
本题的棋子移动数量不限,则可以以一种延迟撤回的思想,即由这个棋子往目标方向跳,若有撞上的则将撞上的移开,若撞上的那个棋子无路可走,则又移动撞上的那个棋子的周围棋子,如此类似递归步骤,可知这个棋子无论如何可以被移开。故充分性得证。
那么本题做法就比较明显了:先预处理出原矩阵\((1,1,n,m)\)中零一类点个数分别记为\(Q_0,Q_1\),将零一类棋子数量分别记为\(S_0,S_1\)。对于每一次输入的待求矩阵,统计出它总共的零一类点的个数为\(T_0,T_1\),并求出需要移动棋子到达的位置(对应\(c_i,d_i\))中零一类位置有\(P_0,P_1\)个,则这个问题有解的充要条件为:
\[\left\{ \begin{aligned} P_0&\le S_0 \\ P_1&\le S_1 \\ \end{aligned} \right. \]\[\left\{ \begin{aligned} S_0-P_0\le Q_0-T_0\\ S_1-P_1\le Q_1-T_1\\ \end{aligned} \right. \]然后我们讨论对于这些变量的求法。首先若\(nm\)为偶数,那么\(Q_0,Q_1\)就都等于\(\frac{nm}{2}\),否则\(Q_0=\left\lceil\frac{nm}{2}\right\rceil,Q_1=nm-Q_0\)(第一个出现的是\((1,1)\),属于零类点。)
而对于\(T_0,T_1\)的计算方式类似于\(Q_0,Q_1\),记\(l=x_2-x_1+1,r=y_2-y_1+1\),若\(lr\equiv 0 (\bmod 2)\)则平分,否则先将\(T_0,T_1\)全部赋值为\(\left\lfloor\frac{lr}{2}\right\rfloor\),然后根据\((x_1,y_1)\)所属哪一类点,则将所对应的\(T\)加上\(1\)。至于\(S_0,S_1,P_0,P_1\)就没得说了,非常简单。
十年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");
}
}
标签:le,R1,int,询问,位置,矩阵,棋子,JEOI
From: https://www.cnblogs.com/oierpyt/p/16947743.html