一看到这道题就想到了 LA 3510 想用大白书上关于置换和循环的部分来解决。然后n太大了1e5,还是三维的,铁定超时。。。
但是这道题目的特点就是对于每一个点的转移方式都是相同的,换句话说就是一个相同的转移矩阵就可以应付所有的坐标。但是具体循环节是多少,还和具体的坐标有关,比如(0,0,0)的循环节就是1,等等。显然答案就是所有循环节的最小公倍数嘛。但死找循环节要超时的啊,我们只能另谋出路。
既然每个点的转移方式都相同,那么每次转移就是左乘一次转移矩阵。如果回到了初始状态就说明转移矩阵乘起来是单位矩阵,此时保证任何坐标都能回到初始状态。而且只有此时才能保证。我也不知道该怎么证明,只是觉得一定存在某个坐标使得乘以非单位矩阵后一定不能等于自己。
然后问题就是已知三阶矩阵A,求最小的x,使得 A^x=E(mod n) 其中E是三阶单位矩阵。
使用优化后的大步小步算法 http://blog.miskcoo.com/2015/05/discrete-logarithm-problem
就可以在不用求逆元和逆矩阵的情况下求得x。
然后为啥枚举的范围在sqrt(mod)内就搜不到答案,一定得在mod范围内才搜得到答案呢?我也是一脸懵逼。。。
不过也就O(n)logn,大点也没啥啦。。
还有啊vector简直就是天然的矩阵类(n维空间类)。
代码
#include<bits/stdc++.h>
#define f(i) for(ll i=0;i<3;i++)
using namespace std;
typedef long long ll;
typedef vector<ll> vi;
ll mod;
vi I()
{
vi ret(9);
ret[0]=ret[4]=ret[8]=1;
return ret;
}
vi base()
{
vi ret(9);
ret[0]=ret[1]=ret[2]=ret[3]=1;
ret[4]=ret[5]=ret[6]=2;
ret[7]=3;
ret[8]=4;
return ret;
}
vi mul(vi a,vi b)
{
vi ret(9);
f(i)f(j)f(k)
ret[3*i+j]=(ret[3*i+j]+(a[3*i+k]*b[3*k+j])%mod)%mod;
return ret;
}
vi mp(vi x,ll n)
{
vi ret=I();
while(n)
{
if(n&1) ret=mul(ret,x);
x=mul(x,x);
n>>=1;
}
return ret;
}
ll solve()
{
ll m=sqrt(mod+0.5);
map<vi,ll>MAP;
vi A=I();
for(ll i=0;i<m;i++)
{
MAP[A]=i;
A=mul(A,base());
}
vi B=mp(base(),m);
A=B;
for(ll i=1;;i++)
{
if(MAP.find(A)!=MAP.end())
return i*m-MAP[A];
A=mul(A,B);
}
return -1;
}
int main()
{
ll T;
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&mod);
printf("%lld\n",solve());
}
return 0;
}