【JSCPC】2021江苏省赛 D. Pattern Lock | 构造

3,122 阅读1分钟

【JSCPC】2021江苏省赛 D. Pattern Lock | 构造

题目链接

Problem - D - Codeforces

题目

image.png

题目大意

nnmm 列的点阵图,要求构造 n×mn\times m 个整点的排列,排列中任意相邻两个点构成一条线段,满足以下条件:

  • 任一线段不经过除端点外的其他整点。
  • 相邻两条线段形成的角是锐角。

数据范围:2n,m5002 ≤ n, m ≤ 500

思路

有多种解法,下面给出一种合法的构造方式:

image.png

总体思想为尽量走 Z 字,每相邻两个点的横坐标或纵坐标相差 11 即可保证不每个线段不经过其他点。

蓝色部分

如果 nn 为偶数,我们可以每两行为一组进行构造。

具体实现如下:

//从第 u 行到第 d 行,第 l 列到第 r 列用蓝色方式进行覆盖
void solven(int u,int d,int l,int r)
{
	for (int i=u,t=0;i<=d;i+=2,t=!t)
	{
		if (t&1)
		{
			for (int j=r;j>l;--j) ans.push_back({i,j}),ans.push_back({i+1,j-1});
			ans.push_back({i,l});
			ans.push_back({i+1,r});
		}
		else
		{
			for (int j=l;j<r;++j) ans.push_back({i,j}),ans.push_back({i+1,j+1});
			ans.push_back({i,r});
			ans.push_back({i+1,l});
		}
	}
}

这里有一种特殊情况,若 nn 为偶数但不等于 22,且 m=2m=2 时,用这种方法覆盖会出现直角,所以进行讨论时这种情况转到黄色部分的构造方法。

image.png

黄色部分

如果 mm 为偶数,我们可以每两列为一组进行构造,具体实现与蓝色部分类似,详见代码不再赘述。同样的,若 mm 为偶数但不等于 22,且 n=2n=2 时,用黄色部分的构造方法会出现直角,所以在这种情况下应该使用蓝色构造方法。

红色部分

nnmm 均为奇数时,我们从左上角取出一个 3×33\times 3 的矩阵直接手玩出答案,就可以把剩下的部分划分成两个有偶数边长的矩阵。

红色部分为手玩出来的固定解法,直接按图片将点依次加入答案即可。

ans.push_back({1,3});
ans.push_back({3,2});
ans.push_back({1,1});
ans.push_back({2,3});
ans.push_back({3,1});
ans.push_back({1,2});
ans.push_back({3,3});
ans.push_back({2,1});
ans.push_back({2,2});

离开红色部分可以直接用黄色部分的构造方法覆盖右侧的 n×(m3)n\times (m-3) 的点阵,但是从红色部分直接转向蓝色部分会出现钝角,于是用绿色部分进行衔接。

绿色部分

nnmm 均为奇数且 n>3n>3 时,我们需要用蓝色的构造方法覆盖一个 (n3)×m(n-3)\times m 的矩阵,但是从 (2,2)(2,2) 点出发直接转为蓝色构造方法会出现钝角,于是用绿色构造方法进行衔接。

该部分也是手玩出来的……同样按图依次加入答案。

ans.push_back({4,1});
ans.push_back({4,2});
ans.push_back({5,1});
ans.push_back({4,3});
ans.push_back({5,2});
ans.push_back({5,3});

代码

#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;
struct asdf{
	int x,y;
};
vector<asdf> ans;
int n,m;
void solven(int u,int d,int l,int r)
{
	for (int i=u,t=0;i<=d;i+=2,t=!t)
	{
		if (t&1)
		{
			for (int j=r;j>l;--j) ans.push_back({i,j}),ans.push_back({i+1,j-1});
			ans.push_back({i,l});
			ans.push_back({i+1,r});
		}
		else
		{
			for (int j=l;j<r;++j) ans.push_back({i,j}),ans.push_back({i+1,j+1});
			ans.push_back({i,r});
			ans.push_back({i+1,l});
		}
	}
}
void solvem(int u,int d,int l,int r)
{
	for (int i=r,t=((r-l+1)/2)%2;i>=l;i-=2,t=!t)
	{
		if (t&1)
		{
			ans.push_back({d,i});
			ans.push_back({u,i-1});
			for (int j=u;j<d;++j) ans.push_back({j,i}),ans.push_back({j+1,i-1});
		}
		else
		{
			ans.push_back({u,i});
			ans.push_back({d,i-1});
			for (int j=d;j>u;--j) ans.push_back({j,i}),ans.push_back({j-1,i-1});
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	if (n%2==0&&m!=2) solven(1,n,1,m);
	else if (m%2==0) solvem(1,n,1,m);
	else
	{
		solvem(1,n,4,m);
		ans.push_back({1,3});
		ans.push_back({3,2});
		ans.push_back({1,1});
		ans.push_back({2,3});
		ans.push_back({3,1});
		ans.push_back({1,2});
		ans.push_back({3,3});
		ans.push_back({2,1});
		ans.push_back({2,2});
		if (n>3)
		{
			ans.push_back({4,1});
			ans.push_back({4,2});
			ans.push_back({5,1});
			ans.push_back({4,3});
			ans.push_back({5,2});
			ans.push_back({5,3});
		}
		solven(6,n,1,3);
	}
	for (auto v:ans) printf("%d %d\n",v.x,v.y);
	return 0;
}