Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 559 Accepted Submission(s): 159
Problem Description
Do you know Vladimir Arnold? He's a mathematician who demonstrated an image transformation method called arnold transformation, which could shuffle all pixels in an image, and after a serials of this transformation, the image would be transformed to its original form.
The transformation method is quite simple. For a given image with N × N pixels (Width and height are both equal to N), a pixel at location (x, y) will be shuffled to location ( (x + y) % N , (x + 2 × y) % N ) (0 ≤ x < N, 0 ≤ y < N). In one step of transformation, all N × N pixels will be shuffled to new corresponding location, making the image a chaotic one. You can do the transformation as many times as you can.
The arnold transformation is very interesting. For every image of size N × N, after finite steps of transformation, the image will become exact the same as the original one. The minimum number of steps which make every possible image become the same as origin will be called as period of arnold transformation. For a given N, can you calculate the period?
Input
There will be about 200 test cases. For each test case, there will be an integer N in one line. Here N (2 ≤ N ≤ 4000000000) equals to width and height of images.
Output
For each test case, please calculate the period of arnold transformation and output it in one line.
Sample Input
Sample Output
Source
2013 Asia Changsha Regional Contest
看了这么久,居然不是找规律的题目……
大致题意是给你一个N*N的矩阵,然后告诉你阿诺德变换,即原来坐标为(x,y)的点变换一次后变成((x+y)%N,(x+2y)%N)。然后告诉你阿诺德变换一定能够通过有限次变换之后变换回原本的矩阵,然后让你求这个周期。
我们用(x,y)的形式表示每一个坐标,写出经过阿诺德变换之后的坐标。我们发现,对于坐标(x,y),经过有阿诺德变换之后,在模n的剩余系下可以写成(fib(n)x+fib(n+1)y,fib(n+1)x+fib(n+2)y)的形式fib(n)表示Fibonacci数列的第n项,再进一步发现,这里的n/2就是相应的变换次数。然后,经过小范围的打表,我们发现点(0,1)所在的循环的周期就是最后答案的周期。所以说我们求(0,1)的周期即可。那么原式子在模n的剩余系下就变成了(fib(n+1),fib(n+2)),即找一个n使得fib(0)=fib(n+1),fib(1)=fib(n+2),也即找Fibonacci数列在n剩余系下的周期。
于是此题就几乎变成了一个模板题。对于这个问题,我们用一下的方法解决:
1、把n质因数分解,即
2、分别计算Fibonacci数列模每个的循环节,假设循环节为
3、那么Fibonacci数列模n的循环节长度就是
那么,现在问题就是如何求数列模的循环节。这里我们有一个定理,Fibonacci数列模的循环节长度等于,其中为Fibonacci数列模素数P的最小循环节长度,结果还是要求这个。
对于,我们考虑在p的剩余系下,5是否是p的二次剩余,如果是那么周期就是p-1的因子,否则是2*(p+1)的因子。关于二次剩余的判定,我们用勒让德符号判断,如果a^((p+1)/2)≡1(mod p),那么a是p下的二次剩余。于是我们艰难的完成了求周期的任务。最后用周期除以2就是最后本题的结果。具体细节还有很多见代码:
#include
#define LL unsigned long long //数字可能很大,所以用ULL
#define N 401000
using namespace std;
int pri[N],num[N],sz,tot;
LL a[N],n,mod,p[N];
bool ispri[N];
struct matrix
{
LL a[3][3];
void init(){memset(a,0,sizeof(a));}
friend matrix operator *(matrix x,matrix y)
{
matrix ans; ans.init();
for(int i=1;i<3;i++)
for(int j=1;j<3;j++)
for(int k=1;k<3;k++)
ans.a[i][j]=(ans.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod;
return ans;
}
friend matrix operator ^(matrix x,LL y)
{
matrix ans;
if (y==0)
{
memset(ans.a,0,sizeof(ans.a));
for(int i=1;i<3;i++) ans.a[i][i]=1;
return ans;
} else while ((y&1)==0) y>>=1,x=x*x;
ans=x; y>>=1;
for(;y!=0;y>>=1)
{
x=x*x; if ((y&1)!=0) ans=ans*x;
}
return ans;
}
} x;
void init()
{
memset(ispri,1,sizeof(ispri));
ispri[1]=0;
for(int i=2;i>=1;
}
return ret;
}
void divide(LL n)
{
for(int i=1;(LL)pri[i]*pri[i]<=n&&i<=sz;i++)
{
if (n%pri[i]) continue;
p[++tot]=pri[i]; num[tot]=0;
while (n%pri[i]==0) n/=pri[i],++num[tot];
}
if (n>1)
{
p[++tot]=n;
num[tot]=1;
}
}
bool legendre(LL p,int x) //勒让德符号
{
return qpow(x,(p-1)>>1LL,p)==1;
}
LL getT(LL n) //在n的约数中招循环节
{
int tot=0;
for(int i=1;(LL)i*i<=n;i++)
{
if (n%i) continue;
a[++tot]=i;
if ((LL)i*i!=n) a[++tot]=n/i;
}
sort(a+1,a+tot+1);
for(int i=1;i<=tot;i++)
{
matrix ans=x^(a[i]-1); //矩阵快速幂求fib数列第n项
LL X=(ans.a[1][1]+ans.a[1][2])%mod;
LL Y=(ans.a[2][1]+ans.a[2][2])%mod;
if (X==1&&Y==0) return a[i]; //如果找到循环节那么返回
}
}
int main()
{
sz=0; init(); //线性筛素数表
x.a[1][1]=x.a[1][2]=x.a[2][1]=1;
while(~scanf("%I64d",&n))
{
LL ans=1LL;
tot=0; divide(n); //分解质因数
for(int i=1;i<=tot;i++)
{
LL T=1LL,P;
if (p[i]==2) T=3LL; else
if (p[i]==3) T=8LL; else
if (p[i]==5) T=20LL; else
{
if (legendre(p[i],5)) P=p[i]-1LL; //如果5是p下二次剩余,那么是p-1约数,否则是2*(p+1)
else P=2LL*(p[i]+1LL);
mod=p[i]; T=getT(P);
}
for(int j=1;j>1LL); //如果用了ULL而不用%I64u那么和不用ULL是一样的,不能是%I64d
}
return 0;
}