快速幂与矩阵乘法

快速幂与矩阵乘法

快速幂 算法的时间复杂度推导:n/2^k=1    => n=2^k  => logn=log2^k => k=logn 故 O(logn)   .2018-9-17 22:28

P1226 【模板】快速幂||取余运算

在线测评地址:https://www.luogu.org/problemnew/show/P1226

AC代码如下:

//P1226 【模板】快速幂||取余运算
//https://www.luogu.org/problemnew/show/P1226
//提交,测试点7,WA
//https://www.luogu.org/discuss/show?postid=56034看了讨论,发现
//输入:1 0 1 正确输出:1^0 mod 1=0 你的输出:1^0 mod 1=1
//总而言之,对洛谷加强数据(测试点7)表示不满,有些牛角尖了。2018-9-17 22:52
//将printf("%lld^%lld mod %lld=%lld\n",b,p,k,quick_power(b,p,k));改成
//printf("%lld^%lld mod %lld=%lld\n",b,p,k,quick_power(b,p,k)%k); 
//提交AC。2018-9-17 22:53 
#include <stdio.h>
#define LL long long
LL quick_power(LL b,LL p,LL k){
    LL ans=1;
    while(p){
        if(p&1)ans=ans*b%k;
        b=b*b%k;
        p>>=1;
    }
    return ans;
}
int main(){
    LL b,p,k;
    scanf("%lld%lld%lld",&b,&p,&k);
    printf("%lld^%lld mod %lld=%lld\n",b,p,k,quick_power(b,p,k)%k);
    return 0;
}

//P1965 转圈游戏
//在线测评地址https://www.luogu.org/problemnew/show/P1965
//动笔,模拟,举个比样例更简单的例子,发现如下规律
//x1=(x0+m)%n
//x2=(x1+m)%n   => x2=(x0+2m)%n
//x3=(x3+m)%n   => x3=(x0+3m)%n
//xq=(x0+qm)%n  => xq=(x0+qm%n)%n   => xq=(x0+q%n*m)%q
//q=10^k    10^109 采用 快速幂 算法的时间复杂度 log(10^10^9) 大致 10^9 还是有些险
//快速幂,稳妥起见,不采用int 采用long long
//样例通过,一次成功,提交AC。2018-9-21
#include <stdio.h>
#define LL long long
LL quickPower(LL a,LL b,LL p){
    LL ans=1;
    while(b){
        if(b&1)ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return ans;
}
int main(){
    LL n,m,k,x,ans;
    scanf("%lld%lld%lld%lld",&n,&m,&k,&x);//此处写成 scanf("%d%d%d%d",&n,&m,&k,&x);
    ans=quickPower(10,k,n);
    ans=(x+ans*m)%n;
    printf("%lld\n",ans);
    return 0;
}

矩阵乘法

//P3390 【模板】矩阵快速幂
//在线测评地址https://www.luogu.org/problemnew/show/P3390
//提交,有些担心,但看到,测试点2-9 AC了,总算放下些心。测试点1,10卡住了 
//代码没做任何修改,再次提交,竟然AC了。够神奇的。2018-9-18 19:48 
#include <stdio.h>
#include <string.h>
#define LL long long
#define maxn 110
#define mod 1000000007
LL a[maxn][maxn],f[maxn][maxn],c[maxn][maxn],n,k;
void mul(){//此处写成 void mul(LL f[][],LL a[][])
    LL i,j,k;
    memset(c,0,sizeof(c));
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            for(k=1;k<=n;k++)
                c[i][j]=(c[i][j]+a[i][k]*f[k][j])%mod;
    memcpy(f,c,sizeof(c));
}
void mulself(){//此处写成 void mulself(LL a[][])
    LL i,j,k;
    memset(c,0,sizeof(c));
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            for(k=1;k<=n;k++)
                c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod;
    memcpy(a,c,sizeof(c));
}
void quick_power(){//此处写成 void quick_power(LL a[][],LL k)
    while(k){
        if(k&1)mul();
        mulself();
        k>>=1;
    }
}
int main(){
    LL i,j;
    scanf("%lld%lld",&n,&k);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%lld",&a[i][j]);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(i==j)
                f[i][j]=1;//初始化为单位矩阵// 忘记了 初始化 
            else 
                f[i][j]=0;
    quick_power();
    for(i=1;i<=n;i++){
        for(j=1;j<=n;j++)
            printf("%lld ",f[i][j]);
        printf("\n");
    }
    return 0;
}

//P1962 斐波那契数列
//在线测评地址https://www.luogu.org/problemnew/show/P1962
//[{f[2],f[1]}]*[{1,1},{1,0}] 与 [{1,1},{1,0}]*[{f[2]},{f[1]}] 从易算角度 选择后者
//该题要注意,数组的处理,起始选 0脚标开始 还是 1脚标开始,要特别注意。 2018-9-21
#include <stdio.h>
#include <string.h>
#define mod 1000000007
#define LL long long
LL f[2]={1,1},g[2],a[2][2]={{1,1},{1,0}},d[2][2]={{1,0},{0,1}},c[2][2],n;//d 此刻 单位矩阵
void mul(){//a*d
    LL i,j,k;
    memset(c,0,sizeof(c));
    for(i=0;i<2;i++)//此处写成 for(i=1;i<=2;i++)
        for(j=0;j<2;j++)
            for(k=0;k<2;k++)
                c[i][j]=(c[i][j]+a[i][k]*d[k][j])%mod;
    memcpy(d,c,sizeof(c));
}
void mulself(){//a*a
    LL i,j,k;
    memset(c,0,sizeof(c));
    for(i=0;i<2;i++)
        for(j=0;j<2;j++)
            for(k=0;k<2;k++)
                c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod;
    memcpy(a,c,sizeof(c));
}
void quickPower(LL b){
    while(b){
        if(b&1)mul();
        mulself();
        b>>=1;
    }
}
void mulf(){
    LL i,j;
    memset(g,0,sizeof(g));
    for(i=0;i<2;i++)
        for(j=0;j<2;j++)
            g[i]=(g[i]+d[i][j]*f[j])%mod;
    memcpy(f,g,sizeof(g));
}
int main(){
    scanf("%lld",&n);
    if(n==1||n==2)printf("1\n");//特判
    else{
        quickPower(n-2);
        mulf();
        printf("%lld\n",f[0]);
    }
    return 0;
}

//P1067 Warcraft III 守望者的烦恼
//在线测评地址https://vijos.org/p/1067
//若有该题(1190:上台阶,在线测评地址http://ybt.ssoier.cn:8088/problem_show.php?pid=1190)基础,此题的递推公式不成问题。
//该题递推公式:f(n)=f(n-1)+f(n-2)+...+f(n-k)
//f(0)=1,f(1)=1,f(2)=f(2-1)+f(2-2),f(3)=f(3-1)+f(3-2)+f(3-3),f(4)=f(4-1)+f(4-2)+f(4-3)+f(4-4)
//f(k)=f(k-1)+f(k-2)+...+f(k-k)
//注意:f(1),f(2),...,f(k-1),f(k),通过两重循环可求得 
//f(k+1)=f(k)+f(k-1)+...+f(1)
//f(k+2)=f(k+1)+f(k)+...+f(2)
//f(k+1),f(k+2),...,f(n-1),f(n)需通过矩阵乘法,才可快速求得
//1 1 ... 1 1
//1 0 ... 0 0
//0 1 ... 0 0
//... ... ...
//0 0 ... 1 0
//构造 k 阶矩阵A如上 
//A*[f(k),f(k-1),...,f(1)]=[f(k)+f(k-1)+...+f(1),f(k),...,f(2)]=[f(k+1),f(k),...,f(2)]; 
//故A^(n-k)*[f(k),f(k-1),...,f(1)]=[f(n),f(n-1),...,f(n-k)] 
//提交,测试点3,4AC,测试点2 Wrong Answer,其余测试点 Runtime Error
//修改,for(i=2;i<=k;i++)//此处写成 for(i=2;i<=n;i++),提交,只剩测试点2 Wrong Answer。
//查了讨论,发现 WA#2了的自己测k=n的吧...。
//继续修改,printf("%lld\n",f[k-n+1]);//此处写成 printf("%lld\n",f[n]);提交AC。
//感觉程序在矩阵的元素顺序上挺别扭,还是要改改的。 2018-9-22 13:26 

//该种构造的矩阵,编写程序容易出错,难以一次AC,决定重新构造矩阵,重新编写代码,详见上面的方法。2018-9-22 15:35
#include <stdio.h>
#include <string.h>
#define mod 7777777
#define LL long long
LL k,n,f[15],g[15],A[15][15],c[15][15];
void mulself(){
    LL i,j,p;
    memset(c,0,sizeof(c));
    for(i=1;i<=k;i++)
        for(j=1;j<=k;j++)
            for(p=1;p<=k;p++)
                c[i][j]=(c[i][j]+A[i][p]*A[p][j])%mod;
    memcpy(A,c,sizeof(c));
}
void mul(){
    LL i,j;
    memset(g,0,sizeof(g));
    for(i=1;i<=k;i++)
        for(j=1;j<=k;j++)
            g[i]=(g[i]+A[i][j]*f[j])%mod;
    memcpy(f,g,sizeof(g));
}
void quickPower(LL b){
    while(b){
        if(b&1)mul();
        mulself();
        b>>=1;
    }
}
int main(){
    LL i,j;
    memset(f,0,sizeof(f));
    memset(A,0,sizeof(A));
    memset(g,0,sizeof(g));
    scanf("%lld%lld",&k,&n);
    g[1]=g[0]=1;
    for(i=2;i<=k;i++)//此处写成 for(i=2;i<=n;i++)
        for(j=0;j<i;j++)
            g[i]=(g[i]+g[j])%mod;//f[i]初始化 
    for(i=1;i<=k;i++)f[i]=g[k-i+1];
    for(j=1;j<=k;j++)A[1][j]=1;
    for(i=2;i<=k;i++)A[i][i-1]=1;//此处写成A[i][i+1]=1,不过马上在接下来的测试中发现了//A[i][j]初始化
    if(n<=k)printf("%lld\n",f[k-n+1]);//此处写成 printf("%lld\n",f[n]);
    else{
        quickPower(n-k);
        printf("%lld\n",f[1]);
    } 
    return 0;
}
 

 

猜你喜欢

转载自blog.csdn.net/mrcrack/article/details/82750121