hdu 4794 Arnold (二次剩余,斐波那契循环节)
题意:
给定一个 N∗N(N≤4e9) 的矩阵,现在经过这样一个变换:将 (x,y) 变为 ((x+y)%N,(x+2×y)%N)(0≤x<N,0≤y<N) 现在求经过多少次这样的变换之后在回到 N∗N 的原始矩阵。
思路:
在模n的剩余系下可以写成(fib(n)x+fib(n+1)y,fib(n+1)x+fib(n+2)y)的形式fib(n)表示Fibonacci数列的第n项
所以就成了斐波那契数列循环节。。经典题。注意会爆long long,要用ULL
又写了遍板子,去年的东西都忘得差不多了orz
/* ***********************************************
Author :111qqz
File Name :code/hdu/4794.cpp
************************************************ */
1#include <cstdio>
2#include <cstring>
3#include <iostream>
4#include <algorithm>
5#include <vector>
6#include <queue>
7#include <set>
8#include <map>
9#include <string>
10#include <cmath>
11#include <cstdlib>
12#include <ctime>
13#define fst first
14#define sec second
15#define lson l,m,rt<<1
16#define rson m+1,r,rt<<1|1
17#define ms(a,x) memset(a,x,sizeof(a))
18typedef unsigned long long LL;
19#define pi pair < int ,int >
20#define MP make_pair
1using namespace std;
2const double eps = 1E-8;
3const int dx4[4]={1,0,0,-1};
4const int dy4[4]={0,-1,1,0};
5const int inf = 0x3f3f3f3f;
6struct Mat
7{
8 LL mat[2][2];
9 void clear()
10 {
11 ms(mat,0);
12 }
13 void pr()
14 {
15 for ( int i = 0 ; i < 2 ; i++)
16 for ( int j = 0 ; j < 2 ; j++)
17 printf("%lld%c",mat[i][j],j==1?'\n':' ');
18 }
19}M,M1;
20const Mat P = {1,1,1,0};
21Mat mul (Mat a,Mat b,LL mod)
22{
23 Mat c;
24 c.clear();
25 for ( int i = 0 ; i < 2 ; i++)
26 for ( int j = 0 ; j < 2 ; j++)
27 for ( int k = 0 ; k < 2 ; k++)
28 c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]%mod)%mod;
29 return c;
30}
31Mat mat_ksm(Mat a,LL b,LL mod)
32{
33 Mat res;
34 res.clear();
35 for ( int i = 0 ; i < 2 ; i++) res.mat[i][i] = 1;
36 while (b>0)
37 {
38 if (b&1) res = mul(res,a,mod);
39 b = b >> 1LL;
40 a = mul(a,a,mod);
41 }
42 return res;
43}
44LL gcd(LL a,LL b)
45{
46 return b?gcd(b,a%b):a;
47}
48const int N = 5E6+7;
49bool prime[N];
50int p[N];
51int pri_tot;
52void Lineisprime() //换成线性晒了。
53{
54 // int cnt = 0 ;
55 ms(prime,true);
56 for ( int i = 2 ; i < N ; i++)
57 {
58 if (prime[i]) p[pri_tot++] = i ;
59 for ( int j = 1 ; j < pri_tot && i*p[j] < N ; j++)
60 {
61 prime[i*p[j]] = false;
62 if (i%p[j]==0) break;
63 }
64 }
65}
1LL ksm( LL a,LL b,LL mod)
2{
3 LL res = 1;
4 while (b>0)
5 {
6 if (b&1) res = (res * a) % mod;
7 b = b >> 1LL;
8 a = a * a % mod;
9 }
10 return res;
11}
12LL legendre(LL a,LL p) //勒让德符号,判断二次剩余
13{
14 if (ksm(a,(p-1)>>1,p)==1) return 1;
15 return -1;
16}
1LL pri[N],num[N];//分解质因数的底数和指数。
2int cnt; //质因子的个数
3void solve(LL n,LL *pri,LL *num)
4{
5 cnt = 0 ;
6 for ( int i = 0 ; 1LL*p[i] * p[i] <= n && i < pri_tot ; i++)
7 {
8 if (n%p[i]==0)
9 {
10 int Num = 0 ;
11 pri[cnt] = p[i];
12 while (n%p[i]==0)
13 {
14 Num++;
15 n/=p[i];
16 }
17 num[cnt] = Num;
18 cnt++;
19 }
20 }
21 if (n>1)
22 {
23 pri[cnt] = n;
24 num[cnt] = 1;
25 cnt++;
26 }
27}
28LL fac[N];
29int cnt2; //n的因子的个数
30void get_fac(LL n)//得到n的所有因子
31{
32 cnt2 = 0 ;
33 for (int i = 1 ; i*i <= n ; i++)
34 {
35 if (n%i==0)
36 {
37 if (i*i!=n)
38 {
39 fac[cnt2++] = i ;
40 fac[cnt2++] = n/i;
41 }
42 else fac[cnt2++] = i;
43 }
44 }
45}
46LL delta;
47const LL LOOP[10]={3,8,20};
48LL ask_loop(int id) //我好傻啊。。并不一定所有因子都有啊。。。
49{
50 return LOOP[id];
51}
52LL find_loop(LL n)
53{
54 //cout<<"n:"<<n<<endl;
55 solve(n,pri,num);
56 //puts("pri:");
57 // for ( int i = 0 ; i < cnt ; i++) printf("i:%d %lld\n",i,pri[i]);
58 LL ans = 1;
59 //cout<<"CNT:"<<cnt<<endl;
60 for ( int i = 0 ; i < cnt ; i++)
61 {
62 LL rec = 1;
63 if (pri[i]==2) rec = 3;
64 else if (pri[i]==3) rec = 8;
65 else if (pri[i]==5) rec = 20;
66 else
67 {
68 if (legendre(5,pri[i])==1)
69 get_fac(pri[i]-1);
70 else get_fac((pri[i]+1LL)*(3-1)); //为什么可以假设循环节不大于2*(p+1)???
71 sort(fac,fac+cnt2);
72 // for ( int qqq = 0; qqq < cnt2 ; qqq++) printf("fac: %lld ",fac[qqq]);
73 for ( int j = 0 ; j < cnt2 ; j++) //挨个验证因子
74 {
75 Mat tmp = mat_ksm(M,fac[j],pri[i]); //下标从0开始,验证fac[j]为循环节,应该看fib[0]==fib[fac[j]]和fib[1]==fib[fac[j]+1]是否成立
76 tmp = mul(tmp,M1,pri[i]);
77 if (tmp.mat[0][0]==1&&tmp.mat[1][0]==0)
78 {
79 rec = fac[j];
80 break;
81 }
82 }
1 }
2 for ( LL j = 1 ; j < num[i] ; j++)
3 rec *=pri[i];
4 ans = ans / gcd(ans,rec) * rec;
5 }
6 return ans;
7}
8void init()
9{
10 M.clear();
11 M.mat[0][0] = M.mat[0][1] = M.mat[1][0] = 1;
12 M1.clear();
13 M1.mat[0][0] = 1;
14}
15LL n;
16int main()
17{
1 init();
2 Lineisprime();
3 while (~scanf("%llu",&n))
4 {
5 // cout<<"n:"<<n<<endl
6 if (n==2) puts("3");
7 else
8 printf("%llu\n",find_loop(n)/2);
}
1#ifndef ONLINE_JUDGE
2 fclose(stdin);
3#endif
4 return 0;
5}