题目大意:
给n个点,要求把所有的点分成两部分。划分的依据是:对于所有的距离值,只能属于集合内部,或两个集合之间,而不能都满足。
一开始想要的是并查集处理,但发现有些情况还是没有办法处理,因为他是对边讨论的。
题解提供的方法很巧妙,并没有直接求距离来讨论,而是通过距离平方的奇偶性来判断。
对于所提供的点,其坐标可以分为4个集合:
num[0][0]:x为偶,y为偶
num[0][1]:x为偶,y为奇
num[1][0]:x为奇,y为偶
num[1][1]:x为奇,y为奇
第一种情况:
当num[0][0]+num[1][1]>0&&num[0][1]+num[1][0]>0时,即点可以分成坐标和为偶数,坐标和为奇数两个集合。 那么,就可以直接把这两个集合当成要分成的两部分。因为对于坐标和为偶数的点而言,其相互之间的距离平方必然可以被4整除,而对于坐标和为奇数的点而言,其相互之间的距离的平方必然可以被4整除。但对于分别位于两个部分的点而言,其距离的平方只能被2整除。所以这样分满足条件。
第二种情况:
当num[0][0]+num[0][1]>0&&num[1][0]+num[1][1]>0时, 即坐标和只能为偶数或奇数。当坐标和为偶数时,即num[0][0]和num[1][1]不为空时,那么就可以根据x坐标为偶数或奇数来划分部分。因为
对于处于同一部分的两个点,其之间的距离必能被4整除,而对于不同部分的点,其中间的距离只能被2整除。所以合理,其他的情况分析类似。
第三种情况:
以上两种情况都是基于点集中至少有两个不为空时,还有一种情况是,点集中只有一个不为空。
那么此时可以通过/2来转换为以上的两种情况处理。
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+5;
const int nn=1e6;
int x[N],y[N];
int num[2][2];//四个集合的数的个数
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
set<int>point;
set<int>::iterator it;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
x[i]+=nn;//避免对负数取模
y[i]+=nn;//把负数变成正数,否则会死循环
}
while(1)
{
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
num[x[i]%2][y[i]%2]++;;
if(num[0][0]+num[1][1]>0&&num[0][1]+num[1][0]>0)//分成和为奇数,和为偶数
{#include <bits/stdc++.h>
using namespace std;
const int N=1e3+5;
const int nn=1e6;
int x[N],y[N];
int num[2][2];//四个集合的数的个数
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
set<int>point;
set<int>::iterator it;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
//x[i]+=nn;
//y[i]+=nn;//
}
while(1)
{
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
num[x[i]%2][y[i]%2]++;;
if(num[0][0]+num[1][1]>0&&num[0][1]+num[1][0]>0)
{
for(int i=1;i<=n;i++)
{
if((x[i]+y[i])%2==0)
point.insert(i);
}
printf("%d\n",point.size());
for(it=point.begin();it!=point.end();it++)
printf("%d ",*it);
printf("\n");
break;
}
if(num[0][0]+num[0][1]>0&&num[1][1]+num[1][0]>0)
{
for(int i=1;i<=n;i++)
{
if(x[i]%2==0)
point.insert(i);
}
printf("%d\n",point.size());
for(it=point.begin();it!=point.end();it++)
printf("%d ",*it);
printf("\n");
break;
}//只有一个集合时
int u,v;
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
if(num[i][j]>0)
{
u=i;
v=j;
}
}
}
for(int i=1;i<=n;i++)
{
x[i]=(x[i]-u)/2;
y[i]=(y[i]-v)/2;
}
}
}
return 0;
}
for(int i=1;i<=n;i++)
{
if((x[i]+y[i])%2==0)
point.insert(i);
}
printf("%d\n",point.size());
for(it=point.begin();it!=point.end();it++)
printf("%d ",*it);
printf("\n");
break;
}
if(num[0][0]+num[0][1]>0&&num[1][1]+num[1][0]>0)//和都为奇或偶
{
for(int i=1;i<=n;i++)
{
if(x[i]%2==0)
point.insert(i);
}
printf("%d\n",point.size());
for(it=point.begin();it!=point.end();it++)
printf("%d ",*it);
printf("\n");
break;
}//只有一个集合不为空时
int u,v;
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
if(num[i][j]>0)
{
u=i;
v=j;
}
}
}//转换为其他的情况,因为是作差,那么+-u,或+-v是可以抵消的,
//那么相当于把原来的所有距离全部缩小相同的倍数来研究,所以可以转换
for(int i=1;i<=n;i++)
{
x[i]=(x[i]-u)/2;//
y[i]=(y[i]-v)/2;
}
}
}
return 0;
}