zoukankan      html  css  js  c++  java
  • [学习笔记]矩阵快速幂

    入门的题目就不放了……我放一些进阶的题目好了

    1、P哥破解密码

    比赛的时候还是 (ljc1301) 首切了以后再给我们切的 (\%\%\%)

    没有连续的三个 (A),矩阵为

    (1,1,1)

    (1,0,0)

    (0,1,0)

    (Code Below:)

    #include <cstdio>
    #define Int long long
    Int x[999][999];
    Int ans[999][999];
    Int dx[999][999];
    Int n,k;
    const int p=19260817;
    
    void ans_cf()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dx[i][j]=ans[i][j],ans[i][j]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    ans[i][j]=(ans[i][j]+(x[i][k]*dx[k][j])%p)%p;
    }
    
    void x_cf()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dx[i][j]=x[i][j],x[i][j]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    x[i][j]=(x[i][j]+(dx[i][k]*dx[k][j])%p)%p;
    }	
    
    void fast_pow(Int w)
    {
        while(w)
        {
            if(w%2==1)
                ans_cf();
            w/=2;
            x_cf();
        }
    }
    
    int main()
    {
    	int t;
    	scanf("%d",&t);
    	while(t--){
    		n=3;scanf("%d",&k);
    	    ans[1][1]=x[1][1]=1;
    	    ans[1][2]=x[1][2]=1;
    	    ans[1][3]=x[1][3]=1;
    	    ans[2][1]=x[2][1]=1;
    	    ans[2][2]=x[2][2]=0;
    	    ans[2][3]=x[2][3]=0;
    	    ans[3][1]=x[3][1]=0;
    	    ans[3][2]=x[3][2]=1;
    	    ans[3][3]=x[3][3]=0;
    	    fast_pow(k-1);
    	    printf("%d
    ",(ans[1][1]+ans[2][1]+ans[3][1])%p);
    	}
        return 0;
    }
    

    好早以前的码风了……不要在意

    2、[JLOI2015]有意义的字符串

    思路还是比较好想的,可以得到 ((frac{b+sqrt{d}}{2})^n+(frac{b+sqrt{d}}{2})^n) 必为整数。又题目给定 (b^2leq d<(b+1)^2),所以 (|(frac{b-sqrt{d}}{2})^n|<1)

    现在我们只用考虑 ((frac{b-sqrt{d}}{2})^n>0) 的情况

    首先,(b ot =sqrt{d}),其次,(n\%2=0)。所以我们在矩阵快速幂中特判一下这种情况就好了

    不过这题还要龟速乘,时间复杂度 (O(log^2n))

    (Code Below:)

    #include <bits/stdc++.h>
    #define int long long
    #define ull unsigned long long
    using namespace std;
    const int p=7528443412579576937;
    int n,b,d,ans;
    
    inline int add(int x,int y){
    	return (1ull*x+1ull*y)%p;
    }
    
    int mul(int a,int b){
    	int ret=0;
    	for(;b;b>>=1,a=add(a,a))
    		if(b&1) ret=add(ret,a);
    	return ret;
    }
    
    struct Matrix{
    	int mat[2][2];
    	Matrix(){
    		memset(mat,0,sizeof(mat));
    	}
    	void clear(){
    		for(int i=0;i<2;i++) mat[i][i]=1;
    	}
    };
    Matrix operator * (const Matrix &a,const Matrix &b){
    	Matrix c;
    	for(int i=0;i<2;i++)
    		for(int j=0;j<2;j++)
    			for(int k=0;k<2;k++)
    				c.mat[i][j]=add(c.mat[i][j],mul(a.mat[i][k],b.mat[k][j]));
    	return c;
    }
    Matrix operator ^ (Matrix a,int b){
    	Matrix ret;ret.clear();
    	for(;b;b>>=1,a=a*a)
    		if(b&1) ret=ret*a;
    	return ret;
    }
    Matrix x,y;
    
    signed main()
    {
    	scanf("%lld%lld%lld",&b,&d,&n);
    	if(n==0){
    		printf("1
    ");
    		return 0;
    	}
    	x.mat[0][0]=b;x.mat[0][1]=2;
    	y.mat[0][0]=b;y.mat[0][1]=1;
    	y.mat[1][0]=(d-b*b)/4;
    	y=y^(n-1);x=x*y;ans=x.mat[0][0];
    	if(b*b!=d&&n%2==0) ans=add(ans-1,p);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    3、[HNOI2011]数学作业

    话说以前 (noip) 模拟赛出过这题,只不过我没做出来。。。

    这道题真的太好了,彻底让我懂了矩阵快速幂。

    小矩阵为

    (ans,10^x,1)

    大矩阵为

    (10^{x+1},0,0)

    (1,1,0)

    (0,1,1)

    然后就对于每一个 (10^x) 都重新矩阵快速幂一下,时间复杂度 (O(log^2 n))

    (Code Below:)

    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    ll n,p,h[20],sum;
    
    struct matrix{
    	ll x[4][4];
    	matrix(){
    		memset(x,0,sizeof(x));
    	}
    }a,b,c;
    
    matrix operator * (matrix a,matrix b){
    	matrix c;
    	for(ll i=1;i<=3;i++)
    		for(ll j=1;j<=3;j++)
    			for(ll k=1;k<=3;k++)
    				c.x[i][j]=(c.x[i][j]+a.x[i][k]%p*b.x[k][j]%p)%p;
    	return c;
    }
    
    matrix fast_pow(matrix a,ll b){
    	matrix ret=a;b--;
    	for(;b;b>>=1,a=a*a)
    		if(b&1) ret=ret*a;
    	return ret;
    }
    
    matrix mul(matrix a,matrix b){
    	matrix c;
    	for(ll i=1;i<=3;i++)
    		for(ll j=1;j<=3;j++)
    			c.x[1][i]=(c.x[1][i]+a.x[1][j]%p*b.x[j][i]%p)%p;
    	return c;
    }
    
    int main()
    {
    	scanf("%lld%lld",&n,&p);
    	if(n<=9){
    		ll ans=0;
    		for(ll i=1;i<=n;i++) 
    			ans=(ans*10+i)%p;
    		printf("%lld
    ",ans);
    		return 0;
    	}
    	for(ll i=1;i<=9;i++)
    		sum=(sum*10+i)%p;
    	h[1]=10;
    	for(ll i=2;i<=18;i++)
    		h[i]=h[i-1]*10;
    	a.x[1][1]=sum;a.x[1][2]=h[1];a.x[1][3]=1;
    	b.x[1][1]=h[2];b.x[2][1]=b.x[2][2]=b.x[3][2]=b.x[3][3]=1;
    	for(ll i=2;i<=18;i++){
    		if(h[i-1]<=n&&n<h[i]){
    			c=fast_pow(b,n-h[i-1]+1);
    			a=mul(a,c);
    			printf("%lld
    ",a.x[1][1]%p);
    			return 0;
    		}
    		c=fast_pow(b,h[i]-h[i-1]);
    		a=mul(a,c);
    		a.x[1][2]=h[i]%p;a.x[1][3]=1;
    		b.x[1][1]=b.x[1][1]*10%p;
    	}
    	return 0;
    }
    

    4、[SCOI2009]迷路

    (Code Below:)

    #include <bits/stdc++.h>
    using namespace std;
    const int p=2009;
    int n,T;
    
    struct matrix{
    	int m[110][110];
    	matrix(){
    		memset(m,0,sizeof(m));
    	}
    	void clear(){
    		for(int i=1;i<=n;i++) 
    			m[i][i]=1;
    	}
    }f;
    
    matrix operator * (matrix a,matrix b){
    	matrix c;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=n;k++)
    				c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j])%p;
    	return c;
    }
    
    matrix operator ^ (matrix a,int b){
    	matrix ret;ret.clear();
    	for(;b;b>>=1,a=a*a)
    		if(b&1) ret=ret*a;
    	return ret;
    }
    
    inline int pos(int i,int j){
    	return i+j*(n/9);
    }
    
    int main()
    {
    	scanf("%d%d",&n,&T);
    	n*=9;
    	int x;
    	for(int i=1;i<=n/9;i++){
    		for(int j=1;j<=8;j++)
    			f.m[pos(i,j)][pos(i,j-1)]=1;
    		for(int j=1;j<=n/9;j++){
    			scanf("%1d",&x);
    			f.m[i][pos(j,x-1)]=1;
    		}
    	}
    	f=f^T;
    	printf("%d
    ",f.m[1][n/9]);
    	return 0;
    }
    

    5、[HNOI2008]GT考试

    (kmp) 解决矩阵快速幂问题,我也是头一回遇到

    (Code Below:)

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,mod,fail[30];
    char s[30];
    
    struct Matrix{
    	int mat[30][30];
    	Matrix(){
    		memset(mat,0,sizeof(mat));
    	}
    	void clear(){
    		for(int i=0;i<m;i++) mat[i][i]=1;
    	}
    };
    Matrix operator * (Matrix a,Matrix b){
    	Matrix c;
    	for(int i=0;i<m;i++)
    		for(int j=0;j<m;j++)
    			for(int k=0;k<m;k++)
    				c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
    	return c;
    }
    Matrix operator ^ (Matrix a,int b){
    	Matrix ret;ret.clear();
    	for(;b;b>>=1,a=a*a)
    		if(b&1) ret=ret*a;
    	return ret;
    }
    Matrix a;
    
    inline void read(int &x){
    	x=0;int f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	if(f==-1) x=-x;
    }
    void print(int x){
    	if(x<0){putchar('-');x=-x;}
    	if(x>9) print(x/10);
    	putchar(x%10+'0');
    }
    
    int main()
    {
    	read(n),read(m),read(mod);
    	scanf("%s",s);
    	fail[0]=fail[1]=0;
    	int p=0;
    	for(int i=1;i<m;i++){
    		while(p&&s[p]!=s[i]) p=fail[p];
    		fail[i+1]=(s[p]==s[i])?++p:p;
    	}
    	for(int i=0;i<m;i++){
    		for(int j=0;j<10;j++){
    			p=i;
    			while(p&&s[p]-'0'!=j) p=fail[p];
    			(s[p]-'0'==j)?++p:0;
    			if(p<m) a.mat[i][p]++; 
    		}
    	}
    	a=a^n;
    	int ans=0;
    	for(int i=0;i<m;i++)
    		ans=(ans+a.mat[0][i])%mod;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    6、能量采集

    公开赛的题,不是莫比乌斯反演

    思路比较好想的,接下来可以矩阵快速幂了

    考虑优化:二进制拆分成 (31) 份是可以通过此题的

    我们算了一下块取 (2^{16}) 空间过不了,不过没关系,我们将块设为 (2^{11})

    (\%) 换成 (-) 也能提速哦!

    (Code Below:)

    #include <bits/stdc++.h>
    #define ll long long
    #define res register int
    using namespace std;
    const int p=998244353;
    int n,m,q,H[40];
    
    inline void add(int &x,int y){
    	x=x+y>=p?x+y-p:x+y;
    }
    
    struct Matrix{
    	int n,m,mat[60][60];
    	Matrix(){
    		memset(mat,0,sizeof(mat));
    	}
    	void clear(){
    		for(res i=0;i<n;i++) mat[i][i]=1;
    	}
    };
    inline Matrix operator * (const Matrix &a,const Matrix &b){
    	Matrix c;
    	for(res i=0;i<a.n;i++)
    		for(res j=0;j<b.m;j++)
    			for(res k=0;k<a.m;k++) add(c.mat[i][j],1ll*a.mat[i][k]*b.mat[k][j]%p);
    	c.n=a.n;c.m=b.m;
    	return c;
    }
    Matrix f[40],a;
    
    inline void read(int &x){
    	x=0;int f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	if(f==-1) x=-x;
    }
    void print(int x){
    	if(x<0){putchar('-');x=-x;}
    	if(x>9) print(x/10);
    	putchar(x%10+'0');
    }
    
    int fast_pow(int a,int b){
    	res ret=1;
    	for(;b;b>>=1,a=1ll*a*a%p)
    		if(b&1) ret=1ll*ret*a%p;
    	return ret;
    }
    
    int main()
    {
    	H[0]=1;
    	for(res i=1;i<=31;i++) H[i]=H[i-1]<<1;
    	read(n),read(m),read(q);
    	a.n=a.m=1;f[0].n=f[0].m=n;
    	res x,y;f[0].clear();
    	for(res i=0;i<n;i++) read(a.mat[i][0]);
    	for(res i=1;i<=m;i++){
    		read(x),read(y);
    		f[0].mat[y-1][x-1]++;
    	}
    	for(res i=0;i<n;i++){
    		x=0;
    		for(res j=0;j<n;j++) add(x,f[0].mat[j][i]);
    		x=fast_pow(x,p-2);
    		for(res j=0;j<n;j++) f[0].mat[j][i]=1ll*f[0].mat[j][i]*x%p;
    	}
    	for(res i=1;i<=31;i++) f[i]=f[i-1]*f[i-1];
    	res sum;
    	while(q--){
    		read(x);Matrix ans=a;sum=0;
    		for(res i=0;i<=31;i++)
    			if(x&H[i]) ans=f[i]*ans;
    		for(res i=0;i<n;i++) sum^=ans.mat[i][0];
    		print(sum%p);putchar('
    ');
    	}
    	return 0;
    }
    

    7、块速递推

    (shadowice1984) 说最优解用的是生成函数,但是我只会 (O(1)) 快速幂

    也就是分块快速幂呗,矩阵快速幂完将 (1sim blo) 的所有矩阵算出来,然后两个矩阵相乘就好了。因为只用两个矩阵相乘只询问一个数,我们大可以不用把所有矩阵都算出来,算一个点就好了。

    能不取模就不要取模,这样快了很多!!!

    矩阵:

    (233,1)

    (666,0)

    考虑如何卡常?

    我们发现这个数列循环节为 (p-1),所以将 (n\%(p-1))。然后那个块的大小可以选用 (2^{16}),这样位运算会很快,不容易被卡掉。

    (Code Below:)

    // luogu-judger-enable-o2
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("inline")
    #pragma GCC optimize("-fgcse")
    #pragma GCC optimize("-fgcse-lm")
    #pragma GCC optimize("-fipa-sra")
    #pragma GCC optimize("-ftree-pre")
    #pragma GCC optimize("-ftree-vrp")
    #pragma GCC optimize("-fpeephole2")
    #pragma GCC optimize("-ffast-math")
    #pragma GCC optimize("-fsched-spec")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC optimize("-falign-jumps")
    #pragma GCC optimize("-falign-loops")
    #pragma GCC optimize("-falign-labels")
    #pragma GCC optimize("-fdevirtualize")
    #pragma GCC optimize("-fcaller-saves")
    #pragma GCC optimize("-fcrossjumping")
    #pragma GCC optimize("-fthread-jumps")
    #pragma GCC optimize("-funroll-loops")
    #pragma GCC optimize("-fwhole-program")
    #pragma GCC optimize("-freorder-blocks")
    #pragma GCC optimize("-fschedule-insns")
    #pragma GCC optimize("inline-functions")
    #pragma GCC optimize("-ftree-tail-merge")
    #pragma GCC optimize("-fschedule-insns2")
    #pragma GCC optimize("-fstrict-aliasing")
    #pragma GCC optimize("-fstrict-overflow")
    #pragma GCC optimize("-falign-functions")
    #pragma GCC optimize("-fcse-skip-blocks")
    #pragma GCC optimize("-fcse-follow-jumps")
    #pragma GCC optimize("-fsched-interblock")
    #pragma GCC optimize("-fpartial-inlining")
    #pragma GCC optimize("no-stack-protector")
    #pragma GCC optimize("-freorder-functions")
    #pragma GCC optimize("-findirect-inlining")
    #pragma GCC optimize("-fhoist-adjacent-loads")
    #pragma GCC optimize("-frerun-cse-after-loop")
    #pragma GCC optimize("inline-small-functions")
    #pragma GCC optimize("-finline-small-functions")
    #pragma GCC optimize("-ftree-switch-conversion")
    #pragma GCC optimize("-foptimize-sibling-calls")
    #pragma GCC optimize("-fexpensive-optimizations")
    #pragma GCC optimize("-funsafe-loop-optimizations")
    #pragma GCC optimize("inline-functions-called-once")
    #pragma GCC optimize("-fdelete-null-pointer-checks")
    #include <bits/stdc++.h>
    #define ll long long
    #define ull unsigned long long
    using namespace std;
    const int maxn=(1ll<<16)+10;
    const int p=1e9+7;
    const ull base=(1ll<<16)-1;
    int T,now;ull n;
    
    inline int add(int x,int y){
        x+=y;x>=p?x-=p:0;
        return x;
    }
    
    struct Matrix{
        int mat[2][2];
        Matrix(){
            memset(mat,0,sizeof(mat));
        }
        void clear(){
            mat[0][0]=mat[1][1]=1;
        }
    };
    inline Matrix operator * (const Matrix &a,const Matrix &b){
        Matrix c;
        register int i,j,k;
        for(i=0;i<2;++i)
            for(j=0;j<2;++j)
                for(k=0;k<2;++k) c.mat[i][j]=add(c.mat[i][j],1ll*a.mat[i][k]*b.mat[k][j]%p);
        return c;
    }
    inline Matrix operator ^ (Matrix a,ull b){
        Matrix ret;ret.clear();
        for(;b;b>>=1,a=a*a)
            if(b&1) ret=ret*a;
        return ret;
    }
    Matrix a[maxn],b[maxn],x,y;
    
    ull SA,SB,SC;
    void init(){scanf("%llu%llu%llu",&SA,&SB,&SC);}
    inline ull Rand(){
        SA^=SA<<32,SA^=SA>>13,SA^=SA<<1;
        ull t=SA;SA=SB,SB=SC,SC^=t^SA;
        return SC;
    }
    
    int main()
    {
        register int i;
        x.mat[0][0]=233;x.mat[0][1]=1;x.mat[1][0]=666;
        y=x^(1ll<<16);
        a[0].clear();a[1]=x;
        for(i=2;i<=base;++i) a[i]=a[i-1]*x;
        b[0].clear();b[1]=y;
        for(i=2;i<=base;++i) b[i]=b[i-1]*y;
        scanf("%d",&T);
        init();
        Matrix c,d;
        for(i=1;i<=T;++i){
            n=(Rand()-1)%(p-1);
            const int s=(int)n&base,t=((int)n>>16)&base;
            now^=(1ll*a[s].mat[0][0]*b[t].mat[0][0]+1ll*a[s].mat[0][1]*b[t].mat[1][0])%p;
        }
        printf("%d
    ",now%p);
        return 0;
    }
    
  • 相关阅读:
    计算机编码总结
    将TOMCAT设置成为NT服务
    java 操作oracle 序号器相关
    java事件机制
    c# Semaphore(信号量)
    C#中异步和多线程的区别
    c#多线程调用有参数的方法
    解决TCP网络传输“粘包”问题
    高性能socket设计实现
    c# Buffer学习笔记
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/10166738.html
Copyright © 2011-2022 走看看