题解:AT_arc220_d [ARC220D] Long Trail
更好的阅读体验
这题太好了,arc 的题目就该这样!
我们需要解决这样一个问题:在一个
先考虑一个很牛的转化。由于边不能重复经过,因此我们考虑将一条边
我们希望知道题目要求的“路径”在这个网格图上表示为什么。考虑我们走的相邻两条边
因此我们看出完全图上的一条合法路径应该对应着网格图的一条不经过重复点的路径。而由于完全图经过的相邻三条边一定不会经过同一个点,因此网格图上的路径一定也不存在连续三个格子在同一行或者同一列,也就是说每走一步都必须改变一次方向。
就此,问题转化为,在
我们假设网格图的高度为
\boldsymbol{x} 较小的情况
由于
当
当
当
当
当
当
:::align{center} :::
对于
\boldsymbol{x} 为奇数
我们希望通过一些手法了来缩小
:::align{center} :::
容易发现,这样操作之后
\boldsymbol{x} 为偶数
我们依然希望通过一些办法减小
但是,聪明的人类发现,将这个阶梯状物的最外面一圈格子剥掉,里面还是一个完全一样的子问题。因此我们得到了一个非常巧妙的构造,如图。
:::align{center} :::
我们同样从左上角出发,图中红色折线就是我们的一次操作,让问题缩小到了黄色的部分,经过计算可知,这样的操作让
得到格子上的路径后,我们容易根据这个求出完全图上的路径。至此,问题在
#include<bits/stdc++.h>
#define endl '\n'
#define N 1006
using namespace std;
constexpr int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; //R L D U
//R: 0, L: 1, D: 2, U: 3
int n;
vector<int> vec,ans;
inline void R() {vec.push_back(0);}
inline void L() {vec.push_back(1);}
inline void D() {vec.push_back(2);}
inline void U() {vec.push_back(3);}
void solve(int x)
{
if(x==1)return;
if(x==2)return R(),D();
if(x==3)return R(),D(),R(),D();
if(x==4)return R(),D(),R(),D(),R(),D();
if(x==5)return R(),D(),R(),U(),R(),D(),R(),D(),L(),D(),R(),D();
if(x==6)return R(),D(),R(),U(),R(),D(),R(),D(),L(),D(),R(),D(),R(),D();
if(x&1)
{
for(int i=1;i<=(x-3)/2;i++)R(),D(),R(),U();
R(),D(),R(),D(),L(),D(),L();
for(int i=1;i<=(x-7)/2;i++)U(),L(),D(),L();
D(),solve(x-4);
} else {
for(int i=1;i<=x-3;i++)R(),D();
R(),U();
for(int i=1;i<=(x-4)/2;i++)R(),U(),L(),U();
for(int i=1;i<=(x-8)/2;i++)L(),D(),L(),U();
L(),D(),L(),D(),solve(x-6);
}
}
void solve()
{
scanf("%d",&n),vec.clear(),ans.clear();
solve(n-1),ans.push_back(2);
int x=1,y=2;
for(int i:vec)
{
if(i<2)ans.push_back(x);
else ans.push_back(y);
x+=dx[i],y+=dy[i];
}
assert(ans.size()*2>=(n-2)*(n-2));
printf("%d\n",(int)ans.size());
for(int i:ans)printf("%d ",i);
putchar(10);
}
main()
{
int T; scanf("%d",&T);
while(T--)solve();
return 0;
}