zoukankan      html  css  js  c++  java
  • 矩阵乘法(矩阵快速幂)

    大多数都是递推关系,其中很大的一个应用就是fibonacci数列

    难点其实在于构造适当的矩阵,以下是一些应用

    (1)线性齐次及非齐次递推式的快速求项,求和

    (2)计算几何中点和图形的坐标(旋转,平移,伸压)变换

    (3)不涉及插入、删除的点变换,区间变换和查询

    (4)图论中的路径数问题

    (5)dp的优化之线性递推方程优化(本质和(1)是一样的)

    (6)dp及AC自动机之状态优化(将状态看作图上的点,转移看作边,本质和(1)是一样的)

    【一本通的题】

    1641: 【例 1】矩阵 A×B

    very very 简单的一道题

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    int n,m,p;
    LL a[110][110],b[110][110],c[110][110];
     
    int main(){
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++) scanf("%lld",&a[i][j]);
    	scanf("%d",&p);
    	for(int i=1;i<=m;i++)
    	for(int j=1;j<=p;j++) scanf("%lld",&b[i][j]);
    	
    	for(int i=1;i<=n;i++){ //最外面的两次是最后结果矩阵的大小 
    		for(int j=1;j<=p;j++){
    			for(int z=1;z<=m;z++){
    				c[i][j]+=a[i][z]*b[z][j];
    			}
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=p;j++) printf("%lld ",c[i][j]);
    		printf("
    ");
    	}
    return 0;
    }
    

      

    1642: 【例 2】Fibonacci 第 n 项

    这个就是模板题了

    初始矩阵是 1 0 

    转移矩阵是1 1

                      1 0

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //这个是模板
    //斐波那契数列取模
    /*
    (1)
    
    1 0    *     1 1     =   1 1
                 1 0
    (2)
    1 1    *     1 1     =   2 1
                 1 0
    (3)
    2 1    *     1 1     =   3 2
                 1 0
    所以第n项就是1 0 *       (1,1)^n
                            (1,0)
    
    用快速幂优化就是矩阵快速幂了
    */ 
    LL n,mod;
    LL a[3][3],b[3][3],ans[3][3],c[3][3];
    void add(LL &x,LL y){
    	x=x+y;
    	x-=(x>=mod)?mod:0;
    	return;
    }
    //		memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。
    //但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
    int main(){
    	scanf("%lld %lld",&n,&mod);
    	n-=1;
    	a[1][1]=a[1][2]=a[2][1]=1;a[2][2]=0;
    	ans[1][1]=ans[2][2]=1;  //ans是单位矩阵 
    	ans[2][1]=ans[1][2]=0;
    	
    	while(n){
    		if(n&1){  //快速幂 
    			memset(c,0,sizeof(c));
    			for(int i=1;i<=2;i++){
    				for(int j=1;j<=2;j++){
    					for(int k=1;k<=2;k++) add(c[i][j],ans[i][k]*a[k][j]%mod);
    				}
    			}  
    			memmove(ans,c,sizeof(ans));  //这个函数是实现字节的拷贝   把c放在ans里面 
    		}
    		memset(c,0,sizeof(c));
     		//对a进行倍乘
    		for(int i=1;i<=2;i++)
    		for(int j=1;j<=2;j++){
    			for(int k=1;k<=2;k++) add(c[i][j],a[i][k]*a[k][j]%mod);
    		} 
    		memmove(a,c,sizeof(a));
    		n>>=1;
    	}
    	
    	//这个快速幂主要目的是求出ans数组,然后最后直接与b相乘 
    	
    	b[1][1]=1;b[1][2]=0;
    	memset(c,0,sizeof(c));
    	for(int i=1;i<=1;i++){   //最后大小是1*2 
    		for(int j=1;j<=2;j++){
    			for(int k=1;k<=2;k++) add(c[i][j],ans[i][k]*b[k][j]);
    		}
    	}
    	memmove(b,c,sizeof(b));
    	printf("%lld
    ",b[1][1]);
    return 0;
    }
    

      

    1643:【例 3】Fibonacci 前 n 项和

    1 0 0
    1 1 1
    1 1 0 这个是应该乘的转移矩阵
    第一个矩阵是 1 1 0  第一位是到i的和,第二位是fi-1,第三位是fi-2),每次乘右边这个矩阵就是转移一次

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    /*
    1 0 0
    1 1 1
    1 1 0  这个是应该乘的转移矩阵
    第一个矩阵式 1 1 0 第一位是到i的和,第二位是fi-1,第三位是fi-2),每次乘右边这个矩阵就是转移一次 
    */
    int n;
    LL power[5][5],a[5][5],c[5][5],ans[5][5];
    LL mod;
    void add(LL &x,LL y){
    	x+=y;
    	if(x>=mod) x-=mod;
    	return;
    }
    int main(){
    	scanf("%d",&n);
    	n-=1; //因为第一项和第二项都是1 
    	scanf("%lld",&mod);
    	ans[1][1]=ans[1][2]=1; //ans[1][3]=0
    	a[1][1]=a[2][1]=a[2][2]=a[2][3]=a[3][1]=a[3][2]=1;
    	for(int i=1;i<=3;i++) power[i][i]=1;
    	//单位矩阵 
    	while(n){  //计算ans数组 
    		if(n&1){
    			memset(c,0,sizeof(c));
    			for(int i=1;i<=3;i++)
    			for(int j=1;j<=3;j++)
    			for(int k=1;k<=3;k++){
    				add(c[i][j],power[i][k]*a[k][j]%mod);
    			}
    			memmove(power,c,sizeof(power));
    		}
    		memset(c,0,sizeof(c));
    		for(int i=1;i<=3;i++)
    		for(int j=1;j<=3;j++)
    		for(int k=1;k<=3;k++){
    			add(c[i][j],a[i][k]*a[k][j]%mod);
    		}
    		memmove(a,c,sizeof(a));
    		n>>=1;
    	}
    	memset(c,0,sizeof(c));
    	for(int i=1;i<=1;i++){
    		for(int j=1;j<=3;j++)
    		for(int k=1;k<=3;k++) add(c[i][j],ans[i][k]*power[k][j]%mod);
    	}
    	memmove(ans,c,sizeof(c));
    	printf("%lld
    ",ans[1][1]);
    return 0;
    }
    

      

    1644:【例 4】佳佳的 Fibonacci

     这个数组有不一样的了

    我完全想不到。。。。
    https://www.cnblogs.com/henry-1202/p/9932349.html
    还有这个转移矩阵(人家随随便便就推出来了。。。
    p[i] 1 1 0 0 p[i+1]
    s[i] 0 1 1 0 s[i+1]
    f[i] 0 0 1 1 f[i+1]
    f[i-1]0 0 1 0 f[i]

    f[i] = f[i-1]+f[i-2]
    T[n] = f[1]+f[2]*2+f[3]*3+...+f[n]*n
    S[n] = f[1]+f[2]+f[3]+...+f[n]
    n*S[n] = n*f[1]+n*f[2]+n*f[3]+...+n*f[n]

    --> P[n] = n*S[n]-T[n]
    --> P[n] = (n-1)*f[1]+(n-2)*f[2]+...+(n-n)*f[n]
    因为
    --> P[n-1] = (n-1)*S[n]-T[n-1]
    --> P[n-1] = (n-2)*f[1]+(n-3)*f[2]+...+(n-1-(n-1))*f[n-1]

    --> S[n-1] = f[1]+f[2]+f[3]+....+f[n-1]
    所以
    P[n]=P[n-1]+S[n-1]

    P[i] S[i] f[i] f[i-1]

    1 0 0 0
    1 1 0 0
    0 1 1 1
    0 1 1 0
    */

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //我完全想不到。。。。
    //https://www.cnblogs.com/henry-1202/p/9932349.html
    /*
    还有这个转移矩阵(人家随随便便就推出来了。。。
    p[i]  1 1 0 0   p[i+1]
    s[i]  0 1 1 0   s[i+1]
    f[i]  0 0 1 1   f[i+1]
    f[i-1]0 0 1 0   f[i]
    
    f[i] = f[i-1]+f[i-2]
        T[n] = f[1]+f[2]*2+f[3]*3+...+f[n]*n
        S[n] = f[1]+f[2]+f[3]+...+f[n]
        n*S[n] = n*f[1]+n*f[2]+n*f[3]+...+n*f[n]
        设
        --> P[n] = n*S[n]-T[n]
        --> P[n] = (n-1)*f[1]+(n-2)*f[2]+...+(n-n)*f[n]
        因为
        --> P[n-1] = (n-1)*S[n]-T[n-1]
        --> P[n-1] = (n-2)*f[1]+(n-3)*f[2]+...+(n-1-(n-1))*f[n-1]
        且
        --> S[n-1] = f[1]+f[2]+f[3]+....+f[n-1]
        所以
        P[n]=P[n-1]+S[n-1]
        
        P[i] S[i] f[i] f[i-1]
        
        1 0 0 0
        1 1 0 0
        0 1 1 1
        0 1 1 0
    */ 
    
    LL n,mod;
    LL ans[5][5],power[5][5],a[5][5],c[5][5];
    void add(LL &x,LL y){
    	x+=y;
    	if(x>=mod) x-=mod;
    	return;
    }
    int main(){
    	scanf("%lld",&n);
    	int nn=n;
    	n-=1;
    	scanf("%lld",&mod);
    	ans[1][2]=ans[1][3]=1;//0 1 1 0
    	for(int i=1;i<=4;i++) {
    		power[i][i]=1;  //单位矩阵 
    	}
    	a[1][1]=a[2][2]=a[2][1]=a[3][2]=a[3][3]=a[3][4]=a[4][2]=a[4][3]=1;
    	while(n){
    		if(n&1){
    			memset(c,0,sizeof(c));
    			for(int i=1;i<=4;i++)
    			for(int j=1;j<=4;j++)
    			for(int k=1;k<=4;k++)
    			add(c[i][j],power[i][k]*a[k][j]%mod);
    			memmove(power,c,sizeof(power));
    		}
    		memset(c,0,sizeof(c));
    		for(int i=1;i<=4;i++)
    		for(int j=1;j<=4;j++)
    		for(int k=1;k<=4;k++)
    		add(c[i][j],a[i][k]*a[k][j]%mod);
    		memmove(a,c,sizeof(a));
    		n>>=1;
    	}
    	memset(c,0,sizeof(c));
    	for(int i=1;i<=1;i++)
    	for(int j=1;j<=4;j++)
    	for(int k=1;k<=4;k++)
    	add(c[i][j],ans[i][k]*power[k][j]%mod);
    	memmove(ans,c,sizeof(ans));
    	//结果是ns[n]-p[n] 
    	printf("%lld",(ans[1][2]*nn%mod-ans[1][1]+mod)%mod);
    	return 0;
    }

     

    1645:Fibonacci

    没啥变化还是模板题,就是数据多组,不用看了

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //这个是模板
    //斐波那契数列取模
    /*
    (1)
    
    1 0    *     1 1     =   1 1
                 1 0
    (2)
    1 1    *     1 1     =   2 1
                 1 0
    (3)
    2 1    *     1 1     =   3 2
                 1 0
    所以第n项就是1 0 *       (1,1)^n
                            (1,0)
    
    用快速幂优化就是矩阵快速幂了
    */ 
    LL n,mod;
    LL a[3][3],b[3][3],ans[3][3],c[3][3];
    void add(LL &x,LL y){
    	x=x+y;
    	x-=(x>=mod)?mod:0;
    	return;
    }
    //		memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。
    //但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
    int main(){
    		mod=10000;
    	while(~scanf("%lld",&n)){
    		if(n==0){
    			printf("0
    ");
    			continue;
    		}
    	if(n==-1) break;
    	n-=1;
    	a[1][1]=a[1][2]=a[2][1]=1;a[2][2]=0;
    	ans[1][1]=ans[2][2]=1;  //ans是单位矩阵 
    	ans[2][1]=ans[1][2]=0;
    	
    	while(n){
    		if(n&1){  //快速幂 
    			memset(c,0,sizeof(c));
    			for(int i=1;i<=2;i++){
    				for(int j=1;j<=2;j++){
    					for(int k=1;k<=2;k++) add(c[i][j],ans[i][k]*a[k][j]%mod);
    				}
    			}  
    			memmove(ans,c,sizeof(ans));  //这个函数是实现字节的拷贝   把c放在ans里面 
    		}
    		memset(c,0,sizeof(c));
     		//对a进行倍乘
    		for(int i=1;i<=2;i++)
    		for(int j=1;j<=2;j++){
    			for(int k=1;k<=2;k++) add(c[i][j],a[i][k]*a[k][j]%mod);
    		} 
    		memmove(a,c,sizeof(a));
    		n>>=1;
    	}
    	
    	//这个快速幂主要目的是求出ans数组,然后最后直接与b相乘 
    	
    	b[1][1]=1;b[1][2]=0;
    	memset(c,0,sizeof(c));
    	for(int i=1;i<=1;i++){   //最后大小是1*2 
    		for(int j=1;j<=2;j++){
    			for(int k=1;k<=2;k++) add(c[i][j],ans[i][k]*b[k][j]);
    		}
    	}
    	memmove(b,c,sizeof(b));
    	printf("%lld
    ",b[1][1]);
    		
    	}
    	
    return 0;
    }
    

      

    1646:GT 考试

     

     这个应该属于第(5)种,先推出dp方程式,然后用矩阵快速幂去优化

    https://blog.csdn.net/jianglw1/article/details/98247505
    https://www.cnblogs.com/gaojunonly1/p/10507823.html
    这个解释德比较清楚,但是我还是没看懂,匹配那一部分,为什么要用到KMP
    此题应该是先想到暴力的做法再用矩阵乘法优化的
    所以暴力的思想很重要
    dp[i][j]表示到第i位,匹配了j个的方案数
    先预处理出f[i][j]表示已经匹配了i个,加一个数字变成匹配了j个方案数,i,j<=m-1,用kmp搞搞
    转移就不难了,dp[i][j]+=dp[i-1][k]*f[k][j] 和矩阵乘法很像

     

    #include <bits/stdc++.h>
    using namespace std;
    typedef int ll;
    /*
    https://blog.csdn.net/jianglw1/article/details/98247505
    https://www.cnblogs.com/gaojunonly1/p/10507823.html
    这个解释德比较清楚,但是我还是没看懂,匹配那一部分,为什么要用到KMP
    此题应该是先想到暴力的做法再用矩阵乘法优化的
    所以暴力的思想很重要
    dp[i][j]表示到第i位,匹配了j个的方案数
    先预处理出f[i][j]表示已经匹配了i个,加一个数字变成匹配了j个方案数,i,j<=m-1,用kmp搞搞
    转移就不难了,dp[i][j]+=dp[i-1][k]*f[k][j] 和矩阵乘法很像 
    */ 
    inline ll read()
    {
        ll s=0;
        bool f=0;
        char ch=' ';
        while(!isdigit(ch))
        {
            f|=(ch=='-'); ch=getchar();
        }
        while(isdigit(ch))
        {
            s=(s<<3)+(s<<1)+(ch^48); ch=getchar();
        }
        return (f)?(-s):(s);
    }
    #define R(x) x=read()
    inline void write(ll x)
    {
        if(x<0)
        {
            putchar('-'); x=-x;
        }
        if(x<10)
        {
            putchar(x+'0'); return;
        }
        write(x/10);
        putchar((x%10)+'0');
        return;
    }
    #define W(x) write(x),putchar(' ')
    #define Wl(x) write(x),putchar('
    ')
    const int N=100005,M=25;
    int n,m,Mod;
    int Num[25],f[25][25];
    int ans[25][25],power[25][25],a[25][25],c[25][25];
    inline void Ad(int &x,int y)
    {
        x+=y;
        x-=(x>=Mod)?Mod:0;
        return;
    }
    int Next[25];
    inline void Pre_f()
    {
        int i,j=0;
        Next[1]=0;
        for(i=2;i<=m;i++)
        {
            while((Num[j+1]!=Num[i])&&j) j=Next[j];
            if(Num[j+1]==Num[i]) j++;
            Next[i]=j;
        }
        for(i=0;i<m;i++)
        {
            for(j=0;j<=9;j++)
            {
                int Now=i;
                while((Num[Now+1]!=j)&&Now) Now=Next[Now];
                if(Num[Now+1]==j) Now++;
                f[i][Now]++;
            }
        }
        /*
        for(i=0;i<m;i++,puts(""))
        {
            for(j=0;j<m;j++) W(f[i][j]);
        }
        puts("");
        */
        return;
    }
    int main()
    {
        int i,j,k,Sum=0;
        R(n); R(m); R(Mod);
        for(i=1;i<=m;i++)
        {
            char ch=' ';
            while(!isdigit(ch)) ch=getchar();
            Num[i]=ch-'0';
        }
        Pre_f();
        ans[0][0]=1;
        for(i=0;i<=m-1;i++) power[i][i]=1;
        memmove(a,f,sizeof a);
        while(n)
        {
            if(n&1)
            {
                memset(c,0,sizeof c);
                for(i=0;i<=m-1;i++) for(j=0;j<=m-1;j++) for(k=0;k<=m-1;k++)
                {
                    Ad(c[i][j],power[i][k]*a[k][j]%Mod);
                }
                memmove(power,c,sizeof power);
            }
            memset(c,0,sizeof c);
            for(i=0;i<=m-1;i++) for(j=0;j<=m-1;j++) for(k=0;k<=m-1;k++)
            {
                Ad(c[i][j],a[i][k]*a[k][j]%Mod);
            }
            memmove(a,c,sizeof a);
            n>>=1;
        }
        memset(c,0,sizeof c);
        for(i=0;i<=0;i++) for(j=0;j<=m-1;j++) for(k=0;k<=m-1;k++)
        {
            Ad(c[i][j],ans[i][k]*power[k][j]%Mod);
        }
        memmove(ans,c,sizeof ans);
        for(i=0;i<m;i++) Ad(Sum,ans[0][i]);
        Wl(Sum);
        return 0;
    }
    

      

    1647:迷路

     这个就是类型(4)图论中的题

    其实这里要先知道一个结论的,我不知道TAT

    这个题初看没有什么头绪(dp失败. 我们联系一下邻接矩阵,

    邻接矩阵一个很神奇的性质: 表示连通性的邻接矩阵的k次幂后的a[i][j]表示图中i–>j长度为k的路径条数,大概理解为:
    从第i点出发到任意点的长度k-1道路条数(第i行) 与 任意点到j点的长度1的道路条数(第j列) 乘积和就是i–>j长度为k的路径条数
    , 但是邻接矩阵只能处理边权为1的点, 这题需要转换. 我们把一个点i拆成更多的虚点,用来抵消部分边权的距离, 让它实际上以邻接矩阵的方式跑图. 假如点1到点2的边权为5,
    把这条边拆成边权为1的5条边, 让点1在自己的虚点上先走4步, 最后到达2, 路径(1.0)->(1.1)->(1.2)->(1.3)->(1.4)->(2.0), 最后矩阵快速幂一下。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=505;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //https://www.cnblogs.com/gaojunonly1/p/10507823.html
    //https://blog.csdn.net/jianglw1/article/details/98247593
    /*
    这个题初看没有什么头绪(dp失败. 我们联系一下邻接矩阵, 邻接矩阵一个很神奇的性质: 表示连通性的邻接矩阵的k次幂后的a[i][j]表示图中i–>j长度为k的路径条数,大概理解为:
     从第i点出发到任意点的长度k-1道路条数(第i行) 与 任意点到j点的长度1的道路条数(第j列) 乘积和就是i–>j长度为k的路径条数
    ? 但是邻接矩阵只能处理边权为1的点, 这题需要转换. 我们把一个点i拆成更多的虚点,用来抵消部分边权的距离, 让它实际上以邻接矩阵的方式跑图. 假如点1到点2的边权为5,
     把这条边拆成边权为1的5条边, 让点1在自己的虚点上先走4步, 最后到达2, 路径(1.0)->(1.1)->(1.2)->(1.3)->(1.4)->(2.0), 最后矩阵快速幂一下。
    */
    int n,m,mod=2009,t;
    struct node{
    	int mp[105][105];
    	node(){
    		memset(mp,0,sizeof(mp));
    	}
    	node operator * (const node &tt)const{
    		node ans;
    		for(int i=1;i<=m;i++)
    		for(int j=1;j<=m;j++)
    		for(int k=1;k<=m;k++) 
    		ans.mp[i][j]=(ans.mp[i][j]+mp[i][k]*tt.mp[k][j]%mod)%mod;
    		
    	return ans;
    	}
    };
    node poww(node a,int k){
    	node res;
    	for(int i=1;i<=m;i++) res.mp[i][i]=1;
    	while(k){
    		if(k&1){
    			res=res*a;
    		}
    		a=a*a;
    		k>>=1;
    	}
    	return res;
    }
    int main(){
    	scanf("%d %d",&n,&t);
    	m=9*n;
    	//总共的点数!!
    	node ans,base;
    	for(int i=1;i<=n;i++){ //每个点都拆为9个 
    		for(int j=1;j<9;j++){
    			base.mp[(i-1)*9+j][(i-1)*9+j+1]=1;  //连接拆点 
    		}
    	} 
    	int tmp;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++){
    			scanf("%1d",&tmp);   //!!!!!!!!这里,保证输入这个数只有一位!!! 
    			if(tmp){
    				base.mp[(i-1)*9+tmp][(j-1)*9+1]=1;  //在自己的链上跑tmp次到下个状态
    			} 
    		}
    	}
    	ans=poww(base,t);
    	printf("%d
    ",ans.mp[1][m-8]);
    	//最后一个点的1.0 
    return 0;
    }
    

    POJ 3735  Training little cats

     

    //其实这是一道理解转移矩阵很好的一道题
    //g 给1个 e 吃掉所以 s(i,j) 交换i和j的
    /*
    1 0 0 1 x ---> x+1
    0 1 0 0 y ---> y
    0 0 1 0 z ---> z
    0 0 0 1 1 ---> 1

    1 0 0 0 x ---> x
    0 0 0 0 y ---> 0
    0 0 1 0 z ---> z
    0 0 0 1 1 ---> 1

    1 0 0 0 x ---> x
    0 0 1 0 y ---> z
    0 1 0 0 z ---> y
    0 0 0 1 1 ---> 1

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=110;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //其实这是一道理解转移矩阵很好的一道题
    //g  给1个  e 吃掉所以   s(i,j) 交换i和j的
    /*
    1 0 0 1  x  --->  x+1
    0 1 0 0  y  --->  y
    0 0 1 0  z  --->  z
    0 0 0 1  1  --->  1
    
    1 0 0 0  x  --->  x
    0 0 0 0  y  --->  0
    0 0 1 0  z  --->  z
    0 0 0 1  1  --->  1
    
    1 0 0 0  x  --->  x
    0 0 1 0  y  --->  z
    0 1 0 0  z  --->  y
    0 0 0 1  1  --->  1
    */
    
    struct node{
    	LL mp[maxn][maxn];
    	node(){
    		memset(mp,0,sizeof(mp));
    	}
    	
    }; 
    node e,ans;
    int n,m,k;
    node mul(node a,node b){
    	node res;
    	for(int i=0;i<=n;i++){
    		for(int j=0;j<=n;j++){
    			if(a.mp[i][j]) //优化,不然会超时 
    			for(int k=0;k<=n;k++){
    				res.mp[i][k]+=(a.mp[i][j]*b.mp[j][k]);
    			}
    		}
    	}
    	return res;
    }
    //快速幂 
    node ksm(node a,int k){
    	node res;
    	for(int i=0;i<=n;i++) res.mp[i][i]=1;
    	while(k){
    		if(k&1){
    			res=mul(res,a);
    		}
    		a=mul(a,a);
    		k>>=1;
    	}
    	return res;
    }
    void inti(){
    	char w[2];
    	memset(e.mp,0,sizeof(e.mp));
    	//初始化为单位矩阵
    	int x,y;
    	for(int i=0;i<=n;i++) e.mp[i][i]=1;
    	while(k--){
    		scanf("%s",w);
    		if(w[0]=='g'){  //给一个 
    			scanf("%d",&x);
    			x--;  //eieieieieieiattention
    			e.mp[n][x]++; 
    		}
    		else if(w[0]=='e'){  //吃完 
    			scanf("%d",&x);
    			x--;
    			for(int i=0;i<=n;i++) e.mp[i][x]=0; //都要清0 
    		}
    		else {
    			scanf("%d %d",&x,&y);
    			x--;y--;
    			if(x!=y){
    				for(int i=0;i<=n;i++){
    					swap(e.mp[i][x],e.mp[i][y]);
    				}
    			}
    		}
    	}
    	
    	  
    }
    int main(){
    	while(~scanf("%d %d %d",&n,&m,&k)){
    		if(n==0&&m==0&&k==0) break;
    		inti();
    		ans=ksm(e,m); //变换矩阵做m次变化 
    		for(int i=0;i<n;i++){
    			printf("%lld ",ans.mp[n][i]);  //输出前n个 
    		}
    		printf("
    ");
    	}
    return 0;
    }
    

      

  • 相关阅读:
    Access小用之感
    PHP学习之路今日开启
    IBatis初体验2
    PHP之PDO介绍
    javascript key code 大全
    如何将虚拟机中的Linux系统与shell终端连接
    linux磁盘情况查询
    vi和vim编辑器的使用
    Linux磁盘分区,挂载
    (转)Android Dalvik虚拟机初识
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/13520988.html
Copyright © 2011-2022 走看看