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;
}
posted @ 2024-10-04 18:35  lichenyu_ac  阅读(15)  评论(0)    收藏  举报