Description
给定 ,请在 的网格中找到 个点,满足任意两点间的距离的平方既不为 也不为 。
注意网格的横纵坐标范围是 。
Solution
奇偶性的本质还是对 取模意义下的值。而对 取模意义下的值做这道题,似乎并不好处理 或者 为偶数的值。不过这个思路启示我们从「模意义」的角度去思考这道题。
从「模意义」的角度来分析时,可以将值域简化到剩余系范围内,而代价是牺牲了部分情况之间的差异性。所以,既然取 为模数无法看出不同的情况,我们考虑换个更大的模数来分析。
比如说 。我们设 ,所以 。我们考虑一对点,假设它们的距离的平方恰好是 ,那么这对点的横纵坐标差一定都可以拆成 (注意:此处的 与 中的 是同一个数)。我们假设横坐标差为 ,纵坐标差为 。那么有下式:
一个整数的平方在模 意义下的值仅可能 。 所以当 时上式无论如何都不可能成立。我们只需要讨论 的情况。
- 。此时 中必定一奇数一偶数,也就是横纵坐标差的奇偶性不同。那么我们强制让选择的点的横纵坐标差的奇偶性相同即可。故此时我们根据 的奇偶性来染色即可。
- 。此时 必定两个奇数,也就是横坐标的差都是奇数。那么此时我们根据 的奇偶性染色就可以了。
注意到上面我们只解决了 的问题,并将所有的点分成了至多两类。事实上,我们用相同的方法考虑 ,那么至多将所有的点分成四类。而根据鸽巢原理 个点中选 个点是一定可行的。
复杂度 。
using namespace std;
int n, d1, d2, c1 = 1, c2 = 1;
int cnt[5];
inline int getcol(int x, int y){
int ret = 0;
if(d1 == 1) ret += ((x / c1 + y / c1) & 1) * 2;
else ret += ((x / c1) & 1) * 2;
if(d2 == 1) ret += ((x / c2 + y / c2) & 1);
else ret += ((x / c2) & 1);
return ret;
}
void solve(int col){
int tot = 0;
for(int i = 0; i < n + n; ++i)
for(int j = 0; j < n + n; ++j)
if(getcol(i, j) == col) {
printf("%d %d\n", i, j);
++tot; if(tot == n * n) exit(0);
}
}
int main(){
fin >> n >> d1 >> d2;
while(d1 % 4 == 0) d1 /= 4, c1 *= 2; d1 %= 4;
while(d2 % 4 == 0) d2 /= 4, c2 *= 2; d2 %= 4;
if(d1 == 3) d1 = 1;
if(d2 == 3) d2 = 1;
for(int i = 0; i < n + n; ++i)
for(int j = 0; j < n + n; ++j)
++cnt[getcol(i, j)];
for(int i = 0; i < 4; ++i)
if(cnt[i] >= n * n) solve(i);
return 0;
}