2020牛客暑期多校训练营Points Construction Problem(构造,等周定理)

Points Construction Problem

题目描述

在这里插入图片描述

输入描述:

在这里插入图片描述

输出描述:

在这里插入图片描述

示例1

输入

6
5 20
1 2
1 3
1 4
1 5
3 8

输出

Yes
1 1
2 2
3 3
4 4
5 5
No
No
Yes
1 1
No
Yes
1 1
1 2
2 1

说明

在这里插入图片描述

题目大意

在一个无限平面上,所有的点是白色的,现在你可以将平面上任意n个点染黑。此时定义任意相邻的两个点如果颜色不同,则称其为一个点对。现要求你找到n个点,使得它们染黑之后恰好有m个点对。若无解,则输出"No"。否则输出"Yes",并输出你选择的点的坐标。(SPJ)

分析

首先来看无解的情况。

无解

先可以求出n个点的能形成的点对的数量的上下限。
上限:很容易可以想到如果两个黑点靠在一起,肯定会使得点对变少。因此n个点最多形成的点对就是互相不相邻时,有n*4个点对。

下限:运用小学二年级的等周定理,轻易得出,当一个矩形周长一定时,正方形的面积最大。逆推,可知面积一定时,正方形的周长最小。因此,当n个点都挤成一坨时(近似正方形),理论最小值为4*sqrt(n)。

其他无解:由于一个点时有4个点对,每增加一个点,就会多出2或4或0个点对,因此不可能是奇数个。所以当m为奇数时也无解。

有解

终于轮到有解情况了。首先我们可以把这些点都堆到一起,使得点对的数量最小。然后考虑将这些点“放逐”掉,即使这些点到空旷的地方使得它可以贡献4个点对。
此时,我们按顺序放逐点。从最后一个开始,如果有2个点与待放逐点相邻,则这个点放逐后多出4个点对;如果只有一个,则这个点放逐后多2个点对。(这里自己画图试下就可以了)

基于此,可以把点对的数量增加到m。但是我们这个算法,大概率每次加4,万一过头了,这就需要特赦令,将原来放逐的点收回。那么收回到哪里呢?显然,过头时只会比m多2,因此我们只要将点对少2即可。通过之前的经验,只要把这个点放到一个位置,使得只有一个点与之相邻即可。

后文代码中是将点全部放逐到第三象限去了,原矩阵是在第一象限。

WAWA点

不止最后一行,最后一列放逐时也只有一个点相邻,只能+2。
循环到st时要注意多出来的不止一行。
注意换行!!!
(结合后文代码体会蒟蒻的艰苦WAWA之路看易错点)

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int x[60],y[60];
int main()
{
	int t,n,m;
	for(scanf("%d",&t);t--;){
		scanf("%d%d",&n,&m);
		if((m&1)||m>4*n||m*m<16*n){puts("No");continue;}//判断无解
		puts("Yes");
		int num=0,sum=0,st=sqrt(n),i,j,trash=0;//num 未放逐的点数量 sum 点对的数量 trash 垃圾,即被放逐的点数量
		for(i=1;i<=st;i++)
		 	for(j=1;j<=st;j++)
		 		num++,x[num]=i,y[num]=j;//先搞成正方形
		sum=st*4;//此时点对为st*4,st是边长
		for(i;num<n;i++){
			sum+=2;//相较原来,每多一行,就多2个点对
			for(j=1;num<n&&j<=st;j++)//这里的j<=st是上面WAWA点的第三条
				num++,x[num]=i,y[num]=j;
		}//注意不止一行,也是WAWA点的第二条
		while(sum<m) sum+=((y[num]==1||x[num]==1)?2:4),num--,trash++;//WAWA点第一条,sum的值在x[num]==1时也要+2
		//哦豁去世
		if(sum>m){trash--,num++,x[num]=0,y[num]=1;}//过头了
		for(int k=1;k<=num;k++) printf("%d %d\n",x[k],y[k]);
		for(int k=1,xx=-1,yy=-1;k<=trash;k++,xx--,yy--)
			printf("%d %d\n",xx,yy);//放逐点直接扔第三象限去
	}
}

END

有错即评。

猜你喜欢

转载自blog.csdn.net/zhangchizc/article/details/107448633