ARC118D Hamiltonian Cycle
有个质数\(P\),然后有个图,点编号为\(1\dots P-1\)。给出数字\(a,b\)。
对于\(i\in [1,P-1]\),连双向边\((i,ai\mod P),(i,bi\mod P)\)。
构造一条哈密顿回路。
\(P\le 10^5\)
设\(n\)为\(a\)在模意义下的阶,记\(H=\{a^i|i\in[0,n)\}\)。记最小的满足\(b^m\in H\)的\(m\)为\(m\)。
首先有解的必要条件是任意数都能表示成\(a^ib^j\)。可以证明,如果有这样的表示方法,那一定存在\(i\in[0,n),j\in[0,m)\)的表示,且唯一。
存在性显然。唯一性:如果存在\(a^{i_1}b^{j_1}=a^{i_2}b^{j_2}\),那么有\(a^{i_1-i_2}=b^{j_1-j_2}\),可得\(b^{j_1-j_2}\in H\),根据\(m\)的定义可得\(j_1-j_2\equiv 0\pmod m\)。
这也说明了能被表示的数的个数是\(nm\)。因此\(P-1=nm\)是有解的必要条件。然后构造使得它为充分条件:
可以看做一个二维的网格(\(n\)行\(m\)列),其中最下面一行往下走可以到最上面一行同一列,最右边一列往右走可以到左边某一列但不一定同行(因为这样比较阴间所以尝试不走这样的边将其构造得出)。
讲官方题解中的Method3(可以处理\(n,m>1\)时的所有情况):
如果\(n\)为偶数,可以直接蛇形构造,最后在点\((n-1,0)\)跳到\((0,0)\)。
如果\(n\)为奇数,把\(a,b\)交换,可以证明交换后得到\(n'\)一定会是偶数。
证明即证\(a,b\)的阶至少有一个为偶数。
设原根为\(g\),则\(a,b\)可以分别表示为\(g^A,g^B\),阶分别为\(\frac{P-1}{A},\frac{P-1}{B}\)。如果阶都是奇数,那么\(A,B\)都是偶数,当\(P\)为奇素数时,能表示的值\(g^x\)一定满足\(x\)是偶数,于是\(x\)为奇数的就表示不出来,与\(a^ib^j\)可以表示所有数矛盾。
using namespace std;
#include <bits/stdc++.h>
#define N 100005
#define ll long long
int P,a,b,invb;
ll qpow(ll x,ll y=P-2){
ll r=1;
for (;y;y>>=1,x=x*x%P)
if (y&1)
r=r*x%P;
return r;
}
bool vis[N];
int n,m;
void getnm(int a,int b){
memset(vis,0,sizeof(bool)*P);
n=0;
for (int x=1;!vis[x];x=(ll)x*a%P)
vis[x]=1,++n;
m=1;
for (int x=b;!vis[x];x=(ll)x*b%P)
++m;
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d%d%d",&P,&a,&b);
getnm(a,b);
if (n*m<P-1){
printf("No\n");
return 0;
}
if (n==1 && m==1){
printf("Yes\n1 1\n");
return 0;
}
if (n&1)
swap(a,b),getnm(a,b);
invb=qpow(b);
printf("Yes\n");
int x=1;
for (int i=0;i<n;i+=2){
printf("%d ",x);
for (int j=1;j<m;++j){
x=(ll)x*b%P;
printf("%d ",x);
}
x=(ll)x*a%P;
printf("%d ",x);
for (int j=m-2;j>=0;--j){
x=(ll)x*invb%P;
printf("%d ",x);
}
x=(ll)x*a%P;
}
printf("1\n");
return 0;
}