• [bzoj2432]兔农


    将每一个重置为0的点作为一段,那么它会导致后面为以x x为开头的斐波拿起数列的东西,那么设这一段是以x为开头,要快速转移到下一段,就可以解决这道题目
    为了转移,我们要处理出下面的东西:1.求出x关于模k的逆元,也就是找到这个0原来的值,那么x*上一个数就是下一段的开头;2.通过这个值反推出这一段的长度(因为我们要求出第n个数),并通过矩阵乘法求出上一个值
    当(x,k)不等于1,那么就没有逆元,也就是说不会出现特殊情况,直接矩乘即可
    当(x,k)=1,通过exgcd求出逆元后,由于斐波那契数列关于模k的循环节不超过6k,预处理出每一个i满足$iequiv f[j](mod k)$的最小的j即可反推出长度
    当然由于n过于大,有可能要经过很多个这样的东西,但由于这样的值只有k个,因此最终会形成循环,我们只需要在循环中快速查找即可
    主要思路就是这样,实现起来要注意细节(比如答案不是对k取模而是对k)和实现的方法(比如特殊的转移也可以用矩阵来转移)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 1000005
     4 struct ji{
     5     int a[3][3];
     6 }s,zy1,zy2,ans;
     7 int k,p,fi,ne,vis[N],las[N];
     8 long long n,l[N];
     9 ji cheng(ji a,ji b){
    10     ji c;
    11     memset(c.a,0,sizeof(c.a));
    12     for(int i=0;i<3;i++)
    13         for(int j=0;j<3;j++)
    14             for(int k=0;k<3;k++)
    15                 c.a[i][j]=(c.a[i][j]+1LL*a.a[i][k]*b.a[k][j])%p;
    16     return c;
    17 }
    18 void ksm(long long n){
    19     if (!n)return;
    20     ji s=zy1;
    21     while (n){
    22         if (n&1)ans=cheng(ans,s);
    23         s=cheng(s,s);
    24         n/=2;
    25     }
    26 }
    27 int exgcd(int a,int b,int &x,int &y){
    28     if (!b){
    29         x=1;
    30         y=0;
    31         return a;
    32     }
    33     int t=exgcd(b,a%b,y,x);
    34     y-=a/b*x;
    35     return t;
    36 }
    37 int main(){
    38     scanf("%lld%d%d",&n,&k,&p);
    39     ans.a[0][0]=ans.a[1][1]=ans.a[2][2]=1;
    40     s=ans;
    41     zy1.a[0][1]=zy1.a[1][0]=zy1.a[1][1]=zy1.a[2][2]=1;
    42     zy2=zy1;
    43     zy2.a[2][0]=zy2.a[2][1]=p-1;
    44     int a=1,b=1;
    45     for(int i=3;;i++){
    46         int c=(a+b)%k;
    47         if (!vis[c]){
    48             vis[c]=i;
    49             las[c]=b;
    50         }
    51         if ((!c)&&(b==1))break;
    52         a=b;
    53         b=c;
    54     }
    55     fi=1;
    56     int flag=0;
    57     while (1){
    58         if (exgcd(fi,k,a,b)>1)break;
    59         a=(a%k+k)%k;
    60         if (n<vis[a])break;
    61         n-=vis[a];
    62         ksm(vis[a]-1);
    63         ans=cheng(ans,zy2);
    64         ne=1LL*fi*las[a]%k;
    65         if ((flag<2)&&(l[ne])){
    66             flag++;
    67             if (flag<2)swap(ans,s);
    68             else{
    69                 swap(ans,s);
    70                 swap(s,zy1);
    71                 ksm(n/(l[ne]-n)+1);
    72                 n%=l[ne]-n;
    73                 swap(s,zy1);
    74             }
    75             memset(l,0,sizeof(l));
    76         }
    77         l[fi=ne]=n;
    78     }
    79     ksm(n);
    80     printf("%d",(ans.a[1][0]+ans.a[2][0])%p);
    81 }
    View Code
  • 相关阅读:
    题解 P2812 【校园网络【[USACO]Network of Schools加强版】】
    拓展卢卡斯定理(伪)
    [洛谷P3807] 【模板】卢卡斯定理
    一道使用Fibonnaci数列通项公式的趣味题目
    [洛谷P3292] [SCOI2016]幸运数字
    [洛谷P3812] 【模板】线性基
    [洛谷P3857] [TJOI2008]彩灯
    2019.06.17课件:[洛谷P1310]表达式的值 题解
    常数PK系列汇总
    关于BFS和dijkstra(2019.04.20)
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/11810414.html
Copyright © 2020-2023  润新知