Atcoder agc025D

63 篇文章 0 订阅
23 篇文章 1 订阅

连这个场上过了70+人的题都不会,脑子可能没救了。
先只考虑一个 D D D的情况,我们将距离为 D \sqrt D D 的点连边,可以证明得到的是一个二分图。具体的,设 D = 2 2 k ⋅ p ( p m o d    4 ≠ 0 ) D=2^{2k}\cdot p(p \mod 4 \neq 0 ) D=22kp(pmod4=0),根据简单的数论知识,所有距离为 D D D的点对都可以写成 ( 2 k ⋅ u , 2 k ⋅ v ) (2^k\cdot u,2^k \cdot v) (2ku,2kv)的形式,其中 u 2 + v 2 = p u^2+v^2=p u2+v2=p,于是我们可以只考虑   m o d   2 k \bmod 2^k mod2k的等价类。注意当 p m o d    4 ≡ 3 p \mod 4 \equiv 3 pmod43的时候不存在合法点对,若 p   m o d   4 ≡ 1 p \bmod 4 \equiv 1 pmod41 u u u v v v一奇一偶,按 u + v u+v u+v的奇偶性染色即可,若 p   m o d   4 ≡ 2 p \bmod 4 \equiv 2 pmod42 u u u v v v均为奇数,按 u u u的奇偶性染色即可。
那么问题变为我们得到了两张二分图,要求取出一个大小为 V 4 \frac{V}{4} 4V的点集,使得在两张图上均为独立集。给两张图分别二染色后,得到四个集合,取其中大小最大的一个显然就行了。
时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)

#include <bits/stdc++.h>

using namespace std;

void col(int n,int m,bool p[][605]) {
  int d=0;
  while (m%(1<<(2*(d+1)))==0) d++;
  d=(1<<d);
  int v=(m/(d*d))%4;
  if (v==0||v==3) return;
  if (v==1) {
  	for(int i=0;i<n;i++)
  	  for(int j=0;j<n;j++) p[i][j]=(((i/d)+(j/d))&1);
  }
  else {
  	for(int i=0;i<n;i++)
  	  for(int j=0;j<n;j++) p[i][j]=((i/d)&1);
  }
}

bool f[605][605],g[605][605];

int main() {
  int n,D1,D2;
  scanf("%d%d%d",&n,&D1,&D2);
  col(n<<1,D1,f);
  col(n<<1,D2,g);
  int sz[2][2]={0};
  for(int i=0;i<(n<<1);i++)
    for(int j=0;j<(n<<1);j++) sz[f[i][j]][g[i][j]]++;
  int id1=0,id2=0;
  for(int i=0;i<2;i++)
    for(int j=0;j<2;j++)
      if (sz[i][j]>sz[id1][id2]) {
      	id1=i;
      	id2=j;
	  }
  int cnt=0;
  for(int i=0;i<(n<<1);i++) {
    for(int j=0;j<(n<<1);j++)
      if (f[i][j]==id1&&g[i][j]==id2) {
      	printf("%d %d\n",i,j);
      	cnt++;
      	if (cnt>=n*n) break;
	  }
	if (cnt>=n*n) break;
  }
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值