Atcoder传送门
题目大意
你有一个 2N×2N 2 N × 2 N 的方格(从0开始编号), 给你两个整数 dis1,dis2 d i s 1 , d i s 2 , 要求取出 N2 N 2 个点, 使得任意两个点之间的距离不为 dis1−−−−√ d i s 1 或 dis2−−−−√ d i s 2 。
输入输出格式
输入格式
一行三个整数, 分别为 N,dis1,dis2 N , d i s 1 , d i s 2 。
输出格式
N2 N 2 行, 每行两个整数 ai,bi a i , b i ,表示取的一个点的坐标。
输入输出样例
输入样例#1
2 1 2
输出样例#1
0 0
0 2
2 0
2 2
输入样例#2
3 1 5
输出样例#2
0 0
0 2
0 4
1 1
1 3
1 5
2 0
2 2
2 4
数据范围
- 1≤N≤300 1 ≤ N ≤ 300
- 1≤dis1≤2×105 1 ≤ d i s 1 ≤ 2 × 10 5
- 1≤dis2≤2×105 1 ≤ d i s 2 ≤ 2 × 10 5
解题分析
一道比较妙妙的构造题。
首先我们要知道勾股数的正整数分解是唯一的,即对于 dis1 d i s 1 和 dis2 d i s 2 都唯一存在 x1,x2,y1,y2 x 1 , x 2 , y 1 , y 2 , 使得 x21+y21=dis21, x22+y22=dis22 x 1 2 + y 1 2 = d i s 1 2 , x 2 2 + y 2 2 = d i s 2 2 。
考虑我们对原图进行两遍染色,分别处理 dis1 d i s 1 和 dis2 d i s 2 。
- 如果 dis MOD 4=1 d i s M O D 4 = 1 ,说明分解出来的 x、y x 、 y 为一奇一偶, 所以我们染成棋盘形即可。
- 如果 dis MOD 4=2 d i s M O D 4 = 2 ,说明分解出来的 x、y x 、 y 为两个奇数, 我们隔行染色即可。
- 如果 dis MOD 4=0 d i s M O D 4 = 0 , 说明分解出来的 x、y x 、 y 为两个偶数, 我们可以将 x、y x 、 y 都缩小一半,转换为上面两种情况处理。
- dis MOD 4=3 d i s M O D 4 = 3 ?老哥数学没学好吧…根本没这种情况
然后我们就可以 O(N2) O ( N 2 ) 预处理染色, O(N2) O ( N 2 ) 输出两次都没有染色的格子。 可以证明未染色的格子是不少于 N2 N 2 个的。随意输出即可。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 610
bool vis[MX][MX];
int siz, d1, d2, bd;
void deal(R int dis)
{
int tim = 0;//放缩倍率
W (!(dis % 4)) dis /= 4, ++tim;
if(dis & 1)
{
for (R int i = 0; i < bd; ++i)
for (R int j = 0; j < bd; ++j)
if((i >> tim) + (j >> tim) & 1) vis[i][j] = true;
}
else
{
for (R int i = 0; i < bd; ++i)
for (R int j = 0; j < bd; ++j)
if((i >> tim) & 1) vis[i][j] = true;
}
}
int main(void)
{
scanf("%d%d%d", &siz, &d1, &d2);
bd = siz << 1; deal(d1), deal(d2);
int tar = siz * siz, cnt = 0;
for (R int i = 0; i < bd; ++i)
for (R int j = 0; j < bd; ++j)
{
if(!vis[i][j]) printf("%d %d\n", i, j), cnt++;
if(cnt == tar) return 0;
}
}