AGC035D Choosing Points
AGC 035D 题解
很妙的思维题:[AGC025D] Choosing Points
题意简述:在\(2N\times2N\)的网格中,构造出一个大小为\(N^2\)的点集,使得任意两点的距离不为\(\sqrt{D1}\)和\(\sqrt{D2}\)。
Solution
先将距离平方一下,就是\((X_u-X_v)^2+(Y_u-Y_v)^2\ne D1\)且\((X_u-X_v)^2+(Y_u-Y_v)^2\ne D2\)。
点集总数为\(4N^2\),选出一个\(N^2\)的点集,这不是赤裸裸的抽屉原理/鸽巢原理吗?
- 抽屉原理/鸽巢原理:将\(n\)个物品放入\(m\)个抽屉中,至少有一个抽屉中有至多$\left \lceil \frac{n}{m} \right \rceil \(个物品,至少\)\left \lfloor \frac{n}{m} \right \rfloor $个物品。
所以我们构造四个可行的、不相交的四个集合,一定有一个集合的大小大于等于\(N^2\)。
这时有一个奇妙的构想,将两个距离分来考虑,一个距离分为两个集合,最后交起来,变为四个集合。
于是对于每个距离,我们要找出两个可行的、不相交的点集。
这不是二分图吗?
但它一定是个二分图吗?
一定有解,证明如下:
对于每个距离,网格中每个点最多之和 4 个点相连,手玩一下,发现只会出现四元环,无法构造出奇环。
所以这一定是张二分图。
所以每次分开跑就行了,难点在于拆集合和看出它是二分图。
思维题真毒瘤。
代码如下:
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=9e5+10;
const int dx[]={1,-1,-1,1};
const int dy[]={1,1,-1,-1};
int head[N],ver[N*10],nxt[N*10],tot=1;
void add(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
int n,D1,D2,c1[N],c2[N];
vector<pair<int,int>>g[4];
inline int id(int x,int y){return x*(n*2)+y;}
inline pair<int,int>get_pair(int x){return {x/(n*2),x%(n*2)};}
void dfs(int x,int color,int c[]){
c[x]=color;
for(int i=head[x];i;i=nxt[i])
if(c[ver[i]]==-1)dfs(ver[i],color^1,c);
}
void build(int D){
memset(head,0,sizeof(head)),tot=1;
for(int i=0;i*i<=D;i++){
int j=sqrt(D-i*i);
if(i*i+j*j!=D)continue;
for(int x=0;x<n<<1;x++)
for(int y=0;y<n<<1;y++)
for(int k=0;k<4;k++){
int a=x+i*dx[k],b=y+j*dy[k];
if(a>=0&&a<n*2&&b>=0&&b<n*2)
add(id(x,y),id(a,b));
}
}
}
int main(){
scanf("%d%d%d",&n,&D1,&D2);
build(D1);
memset(c1,-1,sizeof(c1));
for(int i=0;i<n*n*4;i++)
if(c1[i]==-1)dfs(i,0,c1);
build(D2);
memset(c2,-1,sizeof(c2));
for(int i=0;i<n*n*4;i++)
if(c2[i]==-1)dfs(i,0,c2);
for(int i=0;i<n*n*4;i++){
int op=(c1[i]<<1)|c2[i];
g[op].push_back(get_pair(i));
}
for(int i=0;i<4;i++)
if(g[i].size()>=n*n){
for(int j=0;j<n*n;j++)printf("%d %d\n",g[i][j].x,g[i][j].y);
break;
}
return 0;
}

浙公网安备 33010602011771号