这题我在比赛的时候因为各种玄学错误,导致折腾了半天,在最后 \(20\) 分钟才过 \(\ldots\ldots\)
首先,这道题太啰嗦了,其实把题意简化一下,我们就会发现。对于 \(i\) 维,除去起点和终点路径长度就是 \(i-1\),要求这些节点互不相同。我们分析一下,\(i\) 维其实就有 \(2^i-2\) 个节点,我们把它们一相除,就会发现结果是 \(i\),就是有 \(i\) 条路径。(以上有可能是我瞎 yy 的)
怎么求出每一条路径呢?可能一下子想到的是枚举并用一个哈希表判断是否重复,但这种做法又费空间又费时间,那么就要用到我在比赛时灵光一闪的方法了(放烟花)
大体思路是用一个一维数组,下标 \(i\) 对应着在操作时路径上的节点 \(i\) 需改变的维度。初始化为 \(0\),对应第 \(1\) 维。一次操作后,就将它加 \(1\),留给下一条路径;并且,经过观察,我们不难发现,\(f_{i+1}\) 对应的维度就是 \(f_i\) 的维度加 \(1\),即可满足题目要求。
但可能会有一个问题,如果一直加下去,\(f_i\) 迟早会超过 \(n\)。怎么办?那就取个模呗!即 \(f_{i+1}=(f_i+1)\mod n\)。
同理,操作后要 \(f_i\gets f_i+1\)。
至于这个方法的原理,我也不知道,只是比赛时的一个猜想。如果有哪位 dalao 想证明此方法,可以自己研究一下。日后如果我想到了证明方法,就会补上来。
如果您到现在还没有理解,结合代码食用更佳。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
long long n,f[65];
long long last; //终点的压缩坐标。
string now; //now 为当前节点的坐标。
long long po(long long x) //手打一个计算幂的函数。
{
long long re=1;
for(register long long i=0;i<x;i++) re*=2;
return re;
}
long long zip(string a) //如题,微改的压缩函数。
{
long long size=a.length();
long long h=0;
for(register long long i=size-1;i>=0;i--)
if(a[i]=='1')
h+=po(i);
return h;
}
int main()
{
cin>>n;
cout<<n<<endl; //n 条路径 。
last=po(n)-1;
for(register long long i=0;i<n;i++)
{
cout<<0<<" "; //起点
for(register long long j=0;j<n;j++)
now+='0'; //这是 now 的初始化,将坐标赋为 0。
for(register long long j=1;j<n;j++)
{
now[f[j]]='1'; //精髓,将需改变的维度改变。
f[j+1]=(f[j]+1)%n; //求出 f[i+1]。
f[j]=(f[j]+1)%n; //为防止下一条路径重复,本身也要改变。
long long g=zip(now);
cout<<g<<" "; //输出压缩后的路径。
}
cout<<last<<endl;
now.clear(); //记住!!!一定要清空。
}
return 0;
}
PS:还有这题我一直搞不懂的是:我在比赛的一开始用 cmath 的 pow 函数来计算幂,但是一直会 WA 三个点;后来用手打了一个求幂函数就过了(大雾