zoukankan      html  css  js  c++  java
  • 矩阵乘法题目汇总

    矩阵,学过线性代数的都知道,矩阵满足结合律,但不满足交换律

    关于矩阵,有很多经典的应用,可以看下大牛的博客http://www.matrix67.com/blog/archives/276

    下面的题目中,最常见的一种应用就是利用矩阵求递推式,可以通过构造矩阵求幂

    在这方面,最常见的就是在斐波那契数列方面,可以看下这个博客,超牛的http://www.cnblogs.com/Knuth/archive/2009/09/04/1559951.html

    很容易构造出关于斐波那契的矩阵,累乘求幂,就可以求出斐波那契的对应的项

    直接开始题目吧

    hdu1575 Tr A

    题目,直接矩阵求幂,再求该矩阵的迹,注意,利用矩阵乘法满足结合律,我看可以利用二进制优化求幂次数。比如:A^7=A^4 * A^2 * A^1

    具体的结合代码很容易理解的

    View Code
    #include<iostream>
    #include<algorithm>
    #include<string>
    using namespace std;

    const int MOD = 9973;
    const int N = 11;
    int ret[N][N],init[N][N],temp[N][N];
    int n;
    void init_()
    {
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ret[i][j]=(i==j);
    }
    void matmul(int a[][N],int b[][N])
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k)
    {
    init_();
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }
    int main()
    {
    int T,k;
    scanf("%d",&T);
    while(T--)
    {
    scanf("%d %d",&n,&k);
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    scanf("%d",&init[i][j]);
    q_mod(k);
    int ans=0;
    for(int i=0;i<n;i++)
    ans=(ans+ret[i][i])%MOD;
    printf("%d\n",ans);
    }
    return 0;
    }

    hdu1005 Number Sequence

    题目:很明显的递推式求值,与斐波那契数列很类似,只需要重新构造一下矩阵A=

    A B

    1 0

    向量a=(1,1),则当n大于2 时,f(n)=A^(n-2) * a

    View Code
    #include<iostream>
    #include<algorithm>
    #include<string>
    using namespace std;

    const int MOD = 7;
    const int N = 2;
    int ret[N][N],init[N][N],temp[N][N];
    int n;
    void init_()
    {
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ret[i][j]=(i==j);
    }
    void matmul(int a[][N],int b[][N])
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k)
    {
    init_();
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }
    int main()
    {
    int a,b,k;
    n=2;
    while(scanf("%d %d %d",&a,&b,&k)==3 && (a||b||k))
    {
    init[0][0]=a,init[0][1]=b;
    init[1][0]=1,init[1][1]=0;
    if(k<3)
    {
    printf("1\n");
    continue;
    }
    k-=2;
    q_mod(k);
    int ans=(ret[0][0]+ret[0][1])%MOD;
    printf("%d\n",ans);
    }
    return 0;
    }

    hdu1757 A Simple Math Problem

    构造如下矩阵即可解决问题咯,很清晰

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 10;
    int n=10,mod;
    int ret[N][N],init[N][N],temp[N][N];
    void init_()
    {
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ret[i][j]=(i==j);
    }
    void matmul(int a[][N],int b[][N])
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,temp,sizeof(temp));
    }

    void q_mod(int k)
    {
    init_();
    for(;k;k>>=1)
    {
    if(k&1)
    {
    matmul(ret,init);
    }
    matmul(init,init);

    }
    }
    int main()
    {
    int k;
    while(scanf("%d %d",&k,&mod)==2)
    {
    for(int i=0;i<n;i++)
    scanf("%d",&init[0][i]);
    if(k<10)
    {
    printf("%d\n",k);
    continue;
    }
    for(int i=1;i<n;i++)
    for(int j=0;j<n;j++)
    if(i==j+1)
    init[i][j]=1;
    else init[i][j]=0;
    k-=9;

    q_mod(k);
    int ans=0;
    for(int i=0;i<n;i++)
    ans=(ans+ret[0][i]*(n-i-1))%mod;
    printf("%d\n",ans);
    }
    return 0;
    }

    hdu 2855 Fibonacci Check-up

    只要知道这个公式,就可以轻松解决了

    View Code
    #include<iostream>
    #include<algorithm>
    #include<math.h>
    using namespace std;

    const int N = 2;
    int init[N][N],ret[N][N],temp[N][N];
    int mod;
    void init_(int n)
    {
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ret[i][j]=(i==j);
    }
    void matmul(int a[][N],int b[][N],int n)
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k,int n)
    {
    init_(n);
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init,n);
    matmul(init,init,n);
    }
    }
    int main()
    {
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
    scanf("%d %d",&n,&mod);
    if(n==0)
    {
    printf("0\n");
    continue;
    }
    init[0][0]=init[0][1]=1;
    init[1][0]=1;
    init[1][1]=0;
    q_mod(n*2-1,2);
    int ans=ret[0][0]%mod;
    printf("%d\n",ans);
    }
    return 0;
    }

    hdu1588 Gauss Fibonacci

    分析:

    首先,我们将问题整理一下,就是对等差数列 ai=k*i+b,求所有的f(ai)之和除以M的余数

    当0<=i<N

    大家有没有想到,因为ai是等差数列,倘若f(ai)也是个等什么序列,那说不定就有公式求了

    f(ai)显然不是等差数列,直接看上去也不是等比数列

    但是如果把f(ai)换成我们刚才所说的Fibonacci矩阵呢?

    是的,可是我们对矩阵是直接求幂即可,由于矩阵加法的性质,我们要求A^ai的右上角元素之和,只要求A^ai之和的右上角元素

    就矩阵这个东西来说,完全可以看作一个等比数列, 首项是:A^b 公比是:A^k 项数是:N

    呵呵,我们可以把问题进一步简化

    因为矩阵的加法对乘法也符合分配律,我们提出一个A^b来,形成这样的式子: A^b*( I + A^k + (A^k)^2 + .... + (A^k)^(N-1) )

    A^b 和 A^k 显然都可以用我们之前说过的方法计算出来,这剩下一部分累加怎么解决呢

    简单起见,设A^k=B 要求 G(N)=I + ... + B^(N-1),设i=N/2 若N为偶数,G(N)=G(i)+G(i)*B^i 若N为奇数,G(N)=I+ G(i)*B + G(i) * (B^(i+1))

    呵呵,这个方法就是比赛当时ACRush用的 而农夫用的则是更精妙的方法,大家可想知道

    我们来设置这样一个矩阵 B I O I 其中O是零矩阵,I是单位矩阵

    将它乘方,得到 B^2 I+B O   I 乘三方,得到 B^3 I+B+B^2 O   I 乘四方,得到 B^4 I+B+B^2+B^3 O   I

    既然已经转换成矩阵的幂了,继续用我们的二分或者二进制法,直接求出幂就可以了 该部分解释来自http://www.cnblogs.com/Knuth/archive/2009/09/04/1559951.html

    第二种方法
    #include<iostream>
    #include<algorithm>
    #include<math.h>
    using namespace std;

    const int N =4;
    __int64 init[N][N],temp[N][N],ret[N][N];
    __int64 bb[N][N],B[N][N];
    int mod;
    void init_(int n)
    {
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    init[i][j]=1;
    init[0][0]=0;
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ret[i][j]=(i==j);
    }
    void matmul(__int64 a[][N],__int64 b[][N],int n)
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int n,int k)
    {
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init,n);
    matmul(init,init,n);
    }
    }
    void make(int n)
    {
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ret[i][j]=(i==j);
    memset(init,0,sizeof(init));
    for(int i=0;i<n/2;i++)
    for(int j=0;j<n/2;j++)
    init[i][j]=B[i][j];
    for(int i=0;i<n/2;i++)
    for(int j=n/2;j<n;j++)
    init[i][j]=ret[i][j-n/2];
    for(int i=n/2;i<n;i++)
    for(int j=n/2;j<n;j++)
    init[i][j]=ret[i-n/2][j-n/2];
    }

    int main()
    {
    int n,k,b;
    while(scanf("%d %d %d %d",&k,&b,&n,&mod)==4)
    {
    init_(2);
    q_mod(2,b);
    memcpy(bb,ret,sizeof(ret));
    init_(2);
    q_mod(2,k);
    memcpy(B,ret,sizeof(ret));
    make(4);
    q_mod(4,n);
    for(int i=0;i<2;i++)
    for(int j=0;j<2;j++)
    ret[i][j]=ret[i][j+2];
    matmul(bb,ret,2);
    printf("%I64d\n",bb[0][1]%mod);
    }
    return 0;
    }

    hdu2157  How many ways??

    很有意思的题目,利用图的邻接矩阵和矩阵运算的性质

    很裸的题目

    View Code
    #include<iostream>
    #include<algorithm>
    #include<string>
    using namespace std;

    const int N = 21;
    const int MOD = 1000;
    int n,m;
    int init[N][N],ret[N][N],temp[N][N];
    int g[N][N];
    void init_()
    {
    memcpy(init,g,sizeof(g));
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ret[i][j]=(i==j);
    }
    void matmul(int a[][N],int b[][N])
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k)
    {
    init_();
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }


    int main()
    {
    while(scanf("%d %d",&n,&m)==2 && (n||m))
    {
    int a,b,t;
    memset(g,0,sizeof(g));
    for(int i=0;i<m;i++)
    {
    scanf("%d %d",&a,&b);
    g[a][b]=1;
    }
    scanf("%d",&t);
    while(t--)
    {
    int k;
    scanf("%d %d %d",&a,&b,&k);
    q_mod(k);
    printf("%d\n",ret[a][b]);
    }
    }
    return 0;
    }

    hdu3306 Another kind of Fibonacci

    一开始面对这个平方和束手无策呀,后来将利用递推式将平方展开之后,发现还是一个递推式,而且我们S(n)=S(n-1)+A(n)^2

    所以,就可以构造出一个矩阵出来了

    View Code
    #include<iostream>
    #include<algorithm>
    #include<string>
    using namespace std;
    const int N = 4;
    const __int64 MOD = 10007;
    __int64 init[N][N],temp[N][N],ret[N][N];
    __int64 x,y;
    void init_()
    {
    memset(init,0,sizeof(init));
    init[0][0]=init[0][1]=init[2][1]=1;
    init[1][1]=((x%MOD)*x)%MOD;
    init[1][2]=((y%MOD)*y)%MOD;
    init[1][3]=((2*x)%MOD*y)%MOD;
    init[3][1]=x%MOD;
    init[3][3]=y%MOD;
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ret[i][j]=(i==j);
    }
    void matmul(__int64 a[][N],__int64 b[][N])
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<N;i++)
    for(int k=0;k<N;k++)
    if(a[i][k])
    for(int j=0;j<N;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k)
    {
    init_();
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }
    int main()
    {
    __int64 n;
    while(scanf("%I64d %I64d %I64d",&n,&x,&y)==3)
    {
    q_mod(n);
    __int64 ans=0;
    for(int i=0;i<N;i++)
    ans=(ans+ret[0][i])%MOD;
    printf("%I64d\n",ans);
    }
    return 0;
    }

    hdu2604 Queuing

    关键还是求出递推式子,纠结了好久,利用DFA,有限自动机,找出状态之间的转移

    这里解释的很详细http://blog.chinaunix.net/uid-24323834-id-261400.html

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 4;
    int init[N][N],temp[N][N],ret[N][N];
    int mod;
    void init_()
    {
    memset(init,0,sizeof(init));
    init[0][0]=init[0][2]=init[0][3]=1;
    for(int i=1;i<N;i++)
    for(int j=0;j<N;j++)
    if(i==j+1)
    init[i][j]=1;
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ret[i][j]=(i==j);
    }
    void matmul(int a[][N],int b[][N])
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<N;i++)
    for(int k=0;k<N;k++)
    if(a[i][k])
    for(int j=0;j<N;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k)
    {
    init_();
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }
    int main()
    {
    int n;
    int f[5]={0,2,4,6,9};
    while(scanf("%d %d",&n,&mod)==2)
    {
    if(n<=4)
    {
    printf("%d\n",f[n]%mod);
    continue;
    }
    q_mod(n-4);
    int ans=0;
    for(int i=0;i<4;i++)
    ans=(ans+ret[0][i]*f[4-i])%mod;
    printf("%d\n",ans);
    }
    return 0;
    }

    hdu3519 Lucky Coins Sequence

    题意:同样,还是递推式的问题

    给你n个硬币,问你存在连续相同(正面or反面)长度>2的排列数。

     n很大,显然dp还要加上优化,n<=(10^9),很显然的是用矩阵。

     dp[i][j]表示前i个硬币最后j个是连续的。

     那么dp[i][j]只有两种转移方法:dp[i+1][j+1],dp[i+1][1];

    因为长度一旦超过2,我们就可以确定这个排列是符合要求的,后面的任何一位不管是正面还是反面都满足要求,所以就可以算出dp[i][3]*2^(n-i);

    一旦出现了连续三个就可以满足这个式子。

    dp[i][3]=dp[i-1][2];

    dp[i][2]=dp[i-1][1];

    dp[i][1]=dp[i-1][1]+dp[i-1][2];

    dp[1][1]=2;dp[1][2]=0;dp[1][3]=0;

    这里我们引入多一列dp[i][4]记录前面满足条件的总数;

    dp[i][4]=dp[i-1][3]+dp[i-1][4]*2(这里乘2,就是前面满足条件的排列在第i位可以任意放);

    View Code
    #include<iostream>
    #include<algorithm>
    #include<string>
    using namespace std;
    const int MOD = 10007;
    const int N =4;
    int init[N][N],ret[N][N];
    void init_()
    {
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ret[i][j]=(i==j);
    memset(init,0,sizeof(init));
    init[0][0]=2;
    init[0][1]=init[1][2]=init[2][3]=init[3][3]=init[3][2]=1;
    }
    void matmul(int a[][N],int b[][N])
    {
    int temp[N][N]={0};
    for(int i=0;i<N;i++)
    for(int k=0;k<N;k++)
    if(a[i][k])
    for(int j=0;j<N;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k)
    {
    init_();
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }
    int main()
    {
    int n;
    while(scanf("%d",&n)==1)
    {
    if(n<=2)
    {
    printf("%d\n",0);
    continue;
    }
    q_mod(n);
    printf("%d\n",(ret[0][3]*2)%MOD);
    }
    return 0;
    }

    hdu2294 Pendant

    题意:求出长度为1~n的串中包含K种颜色的珠子的方案数

    构造完矩阵之后,之后就是跟hdu1588类似的过程,注意矩阵中的值,哪一个才是想要求的

    View Code
    #include<iostream>
    #include<algorithm>
    #include<string>
    using namespace std;
    const int N = 61;
    const __int64 MOD = 1234567891;
    __int64 init[N][N],ret[N][N],temp[N][N];
    void init_(int k)
    {
    memset(init,0,sizeof(init));
    for(int i=0;i<k-1;i++)
    {
    init[i][i]=k-i;
    init[i][i+1]=k-i-1;
    }
    init[k-1][k-1]=1;
    for(int i=0;i<k;i++)
    init[i][i+k]=1;
    for(int i=k;i<2*k;i++)
    init[i][i]=1;
    for(int i=0;i<2*k;i++)
    for(int j=0;j<2*k;j++)
    ret[i][j]=(i==j);
    }
    void matmul(__int64 a[][N],__int64 b[][N],int n)
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int n,int k)
    {
    init_(n);
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init,2*n);
    matmul(init,init,2*n);
    }
    }
    int main()
    {
    int T,n,k;
    scanf("%d",&T);
    while(T--)
    {
    scanf("%d %d",&n,&k);
    q_mod(k,n);
    printf("%I64d\n",(ret[0][2*k-1]*k)%MOD);
    }
    return 0;
    }

    hdu2971 Tower

    与3306完全类似,不过要注意最后负数的处理

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 4;
    __int64 mod;
    __int64 ret[N][N],init[N][N];
    void init_(__int64 a2)
    {
    memset(init,0,sizeof(init));
    init[0][0]=init[0][3]=init[1][3]=init[3][1]=1;
    init[2][2]=-1;
    init[0][1]=init[1][1]=(((4*a2)%mod)*a2)%mod;
    init[0][2]=init[1][2]=((-4)*a2)%mod;
    init[2][1]=(2*a2)%mod;
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ret[i][j]=(i==j);
    }
    void matmul(__int64 a[][N],__int64 b[][N])
    {
    __int64 temp[N][N]={0};
    for(int i=0;i<N;i++)
    for(int k=0;k<N;k++)
    if(a[i][k]!=0)
    for(int j=0;j<N;j++)
    if(b[k][j]!=0)
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k,__int64 a2)
    {
    init_(a2);
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }
    int main()
    {
    int T,n;
    __int64 v[4],a2;
    scanf("%d",&T);
    while(T--)
    {
    scanf("%I64d %d %I64d",&a2,&n,&mod);
    if(n==2)
    {
    printf("%I64d\n",(((a2%mod)*a2)%mod+1)%mod);
    continue;
    }
    v[0]=(((a2%mod)*a2)%mod+1)%mod;
    v[1]=((a2%mod)*a2)%mod;
    v[2]=a2%mod;
    v[3]=1;
    q_mod(n-2,a2);
    __int64 ans=0;
    for(int i=0;i<N;i++)
    ans=(ans+ret[0][i]*v[i])%mod;
    if(ans<0)
    printf("%I64d\n",ans+mod);
    else printf("%I64d\n",ans);
    }
    return 0;
    }

    hdu2254 奥运

    同样,利用图的邻接矩阵的性质,不过要先离散化,矩阵不大,可以直接打表

    View Code
    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<map>
    using namespace std;
    const int MOD =2008;
    const int N = 30;
    int g[N][N];
    int ans[10001][N][N];
    map<int,int> ms;
    void matmul(int a[N][N],int m,int n)
    {
    int temp[N][N]={0};
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(g[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*g[k][j])%MOD;
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ans[m][i][j]=temp[i][j];
    }
    int main()
    {
    int n,m,t1,t2,u,v;
    int v1,v2;
    while(scanf("%d",&n)==1)
    {
    ms.clear();
    int num=0;
    memset(g,0,sizeof(g));
    for(int i=0;i<n;i++)
    {
    scanf("%d %d",&u,&v);
    if(ms.find(u)==ms.end())
    ms[u]=num++;
    if(ms.find(v)==ms.end())
    ms[v]=num++;
    g[ms[u]][ms[v]]++;
    }
    memcpy(ans[0],g,sizeof(g));
    for(int i=1;i<=10000;i++)
    {
    matmul(ans[i-1],i,num);
    }
    scanf("%d",&m);
    while(m--)
    {
    __int64 ans1=0;
    scanf("%d %d %d %d",&v1,&v2,&t1,&t2);
    if(ms.find(v1)==ms.end()||ms.find(v2)==ms.end())
    {
    printf("0\n");
    continue;
    }
    if(t1>t2)
    swap(t1,t2);
    for(int i=t1==0?t1:t1-1;i<t2;i++)
    ans1=(ans1+ans[i][ms[v1]][ms[v2]])%MOD;
    printf("%I64d\n",ans1);
    }

    }
    return 0;
    }

    hdu3658  How many words

    求递推式的方式跟之前的很类似,不过首先我们要知道,要使用矩阵来实现状态的转移是有条件的,每一个状态之间的转移必须情况是一样的,同时,是在二维的状态上转移,如果直接求的话,我们需要记录的状态有三个,是否为符合要求的串,也就是串中是否有至少一对相邻的字母的ASCii相差32,以什么字母结尾,还有当前的长度,,很明显,这三个状态都是必不可少的,那该怎么办,正难则反,“至少一个”,对立面是什么,我们可以考虑求出相邻字母小于等于32的字符串的方案数和相邻字母相差小于32的字符串的方案数,那么俩者相减即为所有了,这俩种都可以通过构造矩阵求出

    View Code
    #include<iostream>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    const int N = 52;
    const __int64 MOD = 1000000007;
    __int64 ret[N][N],init[N][N];
    __int64 A[N][N],B[N][N];
    void matmul(__int64 a[][N],__int64 b[][N])
    {
    __int64 temp[N][N]={0};
    for(int i=0;i<N;i++)
    for(int k=0;k<N;k++)
    if(a[i][k])
    for(int j=0;j<N;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int k)
    {
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ret[i][j]=(i==j);
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init);
    matmul(init,init);
    }
    }
    int main()
    {
    char letter[N];
    memset(A,0,sizeof(A));
    memset(B,0,sizeof(B));
    for (int i = 'a';i <= 'z';i++)
    {
    letter[i - 'a'] = i;
    letter[i - 'a' + 26] = i - 'a' + 'A';
    }
    for (int i = 0;i < N;i++)
    {
    for (int j = 0;j < N;j++)
    {
    if (abs(letter[i] - letter[j]) <= 32) A[i][j] = 1;
    if (abs(letter[i] - letter[j]) < 32) B[i][j] = 1;
    }
    }
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
    scanf("%d",&n);
    __int64 ans1=0,ans2=0;
    memcpy(init,A,sizeof(A));
    q_mod(n-1);
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ans1=(ans1+ret[i][j])%MOD;
    memcpy(init,B,sizeof(B));
    q_mod(n-1);
    for(int i=0;i<N;i++)
    for(int j=0;j<N;j++)
    ans2=(ans2+ret[i][j])%MOD;
    printf("%I64d\n",(ans1-ans2+MOD)%MOD);
    }
    return 0;
    }

    hdu2276 Kiki & Little Kiki 2

    这题目,我想,如果事先知道是用矩阵做的话,应该不难想出如何构造矩阵了,关键是在不知道用矩阵做的情况下,该怎么去思考 呢?

    其实,这类题目有一个共同点,就是递推,或者说是状态的转移,而这个题目就很好的诠释了这一点,当前状态是否改变与前一个状态有关,而且,前后状态的转移方式是一样的


    View Code
    #include<iostream>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    const int N = 100;
    const int MOD = 2;
    int init[N][N],ret[N][N],temp[N][N];
    void matmul(int a[][N],int b[][N],int n)
    {
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
    for(int k=0;k<n;k++)
    if(a[i][k])
    for(int j=0;j<n;j++)
    if(b[k][j])
    temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
    memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int n,int k)
    {
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    ret[i][j]=(i==j);
    memset(init,0,sizeof(init));
    for(int i=0;i<n;i++)
    init[i][i]=1;
    for(int i=1;i<n;i++)
    init[i][i-1]=1;
    init[n-1][n-2]=init[0][n-1]=1;
    for(;k;k>>=1)
    {
    if(k&1)
    matmul(ret,init,n);
    matmul(init,init,n);
    }
    }
    int main()
    {
    int n,k;
    char str[N+1],ans[N+1];
    while(scanf("%d",&k)==1)
    {
    scanf("%s",str);
    n=strlen(str);
    q_mod(n,k);
    for(int i=0;i<n;i++)
    {
    int t=0;
    for(int j=0;j<n;j++)
    t+=ret[i][j]*(str[j]-'0');
    ans[i]=t%MOD+'0';
    }
    ans[n]='\0';
    puts(ans);
    }
    return 0;
    }

    hdu 3096  Life Game

    周期L的大小为10^9 这么大的话,首先应该想到的就是矩阵了,其实主要也是因为周期的状态值之间的变换是有规律,这种规律性可以转化为递推式,很明显就可以用矩阵来做了

    构造一个01矩阵:行表示每一个位置的标号,(i,j)表示 i 位置可以由j位置累加

    View Code
    #include <iostream>
    #include<vector>
    #include<algorithm>
    #include<string>
    const int N = 100;
    using namespace std;
    int buttom;
    int n,mod;
    int map[N][N],a[N];
    __int64 temp[N][N];
    __int64 init[N][N],ret[N][N];
    vector<int> g[N];
    int dirup[6][2] = {-1,-1, -1,0,  0,-1,0,1,  1,0, 1,1};
    
    int dirmid[6][2] = {-1,-1, -1,0,  0,-1,0,1,  1,-1, 1,0};
    
    int dirdown[6][2] = {-1,0, -1,1,  0,-1,0,1,  1,-1, 1,0};
    
    int input(int n)
    {
        int num=0;
        for (int i = 1; i <= 2*n - 1; i++ )
        {
            int tt = i > n ? 3*n -i - 1 : n + i -1;
            for (int j = 1 ; j <= tt; j++)
            {
                map[i][j]=num;//给每一个位置标号
                scanf("%d",&a[num++]);
            }
        }
        for(int i=0;i<num;i++)
            g[i].clear();
        return num;
    }
    inline bool isok(int i , int j)
    {
        // 
        if(i < 1 || i > 2*n - 1)
            return false;
        int tt = i > n ? 3*n -i - 1 : n + i -1;
        if(j < 1 || j > tt)
            return false;
        return true;
    }
    void build(int num)
    {
        memset(temp,0,sizeof(temp));
        for (int i = 1; i <= 2*n - 1; i++ )
        {
            int tt = i > n ? 3*n -i - 1 : n + i -1;
            for (int j = 1 ; j <= tt; j++)
            {
                for (int k = 0 ; k < 6 ; k++)
                {
                    int i1,j1;
                    if( i < n )
                        i1 = dirup[k][0] + i , j1 = dirup[k][1] +j;
                    else if(i == n)
                        i1 = dirmid[k][0] + i , j1 = dirmid[k][1] + j;
                    else
                        i1 = dirdown[k][0] + i , j1 = dirdown[k][1] + j;
                    if(isok(i1,j1))
                    {
                        g[map[i1][j1]].push_back(map[i][j]);
                    }
                }
            }
        }
        memset(init,0,sizeof(init));
        for(int i=0;i<num;i++)//构造初始矩阵
        {
            init[i][i]=1;//要加上自身的值
            for(int j=0;j<g[i].size();j++)//标号为i的位置可以由g[i][j]得到
            {
                init[i][g[i][j]]=1;
            }
        }        
    }
    void matmul(__int64 a[][N],__int64 b[][N],int nn)
    {
        memset(temp,0,sizeof(temp));
        for(int i=0;i<nn;i++)
            for(int k=0;k<nn;k++)
                if(a[i][k])
                    for(int j=0;j<nn;j++)
                        if(b[k][j])
                            temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
        memcpy(a,temp,sizeof(temp));
    }
    void q_mod(int nn,int k)
    {
        for(int i=0;i<nn;i++)
            for(int j=0;j<nn;j++)
                ret[i][j]=(i==j);
        for(;k;k>>=1)
        {
            if(k&1)
                matmul(ret,init,nn);
            matmul(init,init,nn);
        }
    }
    
    int main()
    {
        int k,cas=0;
        while(~scanf("%d%d%d",&n,&mod,&k))
        {
            if(!(n||k||mod))
                break;
            int nn=input(n);
            build(nn);
            q_mod(nn,k);
            __int64 ans=0;
            for(int i=0;i<nn;i++)
            {
                __int64 b=0;
                for(int j=0;j<nn;j++)
                {
                    b+=ret[i][j]*a[j];
                    if(b>=mod)
                        b%=mod;
                }
                ans+=b;//注意这里不能取模
            }
            printf("Case %d: %I64d\n",++cas,ans);
        }
        return 0;
    }

    hdu3483 A Very Simple Problem

    题意:计算

    不是自己想出来的,,关于二项式系数,一开始没想到,,,

    View Code
    /*求sum(x^k*k^x) k=1~N  
    x^(k+1)*(k+1)^x=x^k*x*(k+1)^x 然后用二项式定理展开(k+1)^x即可  
    例如当x=4时    
    | 1x  0  0  0  0  0 | |x^k*k^0| |x^(k+1)*(k+1)^0|  
    | 1x 1x  0  0  0  0 | |x^k*k^1| |x^(k+1)*(k+1)^1|  
    | 1x 2x 1x  0  0  0 |*|x^k*k^2|=|x^(k+1)*(k+1)^2|  
    | 1x 3x 3x 1x  0  0 | |x^k*k^3| |x^(k+1)*(k+1)^3|  
    | 1x 4x 6x 4x 1x  0 | |x^k*k^4| |x^(k+1)*(k+1)^4|  
    | 1x 4x 6x 4x 1x 1x | | S(k)  | |     S(k+1)    | */ 
    
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int N = 50+10;
    
    __int64 init[N][N],ret[N][N],temp[N][N];
    int mod;
    
    
    void init_mat(int x)
    {
        memset(init,0,sizeof(init));
        for(int i=0;i<x+2;i++)
            for(int j=0;j<=i;j++)
            {
                if(j==0 || j==i)
                    init[i][j]=x;
                else {
                    if(i!=x+1)
                        init[i][j]=init[i-1][j-1]+init[i-1][j];//利用杨辉三角的性质求二项式系数
                    else init[i][j]=init[i-1][j];
                init[i][j]%=mod;
                }
            }
        init[x+1][x+1]=1;
    }
    void matmul(__int64 a[][N],__int64 b[][N],int n)
    {
        memset(temp,0,sizeof(temp));
        for(int i=0;i<n;i++)
            for(int k=0;k<n;k++)
                if(a[i][k])
                    for(int j=0;j<n;j++)
                        if(b[k][j])
                        {
                            temp[i][j]=temp[i][j]+a[i][k]*b[k][j];
                            if(temp[i][j]>=mod)
                                temp[i][j]%=mod;
                        }
        memcpy(a,temp,sizeof(temp));
    }
    
    void q_mod(int k,int n)
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                ret[i][j]=(i==j);
        for(;k;k>>=1)
        {
            if(k&1)
                matmul(ret,init,n);
            matmul(init,init,n);
        }
    }
    
    int main()
    {
        int n,x;
        while(scanf("%d %d %d",&n,&x,&mod)==3)
        {
            if(n<0 && x<0 && mod<0)
                break;
            init_mat(x);
            q_mod(n-1,x+2);
            __int64 ans=0;
            for(int i=0;i<x+2;i++)
            {
                ans+=ret[x+1][i]*x;
                if(ans>=mod)
                    ans%=mod;
            }
            printf("%I64d\n",ans);
        }
        return 0;
    }

    hdu3509 Buge's Fibonacci Number Problem

    解决了上一个题目,那这个题目也很好解决了

    View Code
    /*
        题意: F[n] = a*F[n-1]+b*F[n-2]   求 Sn = ∑F[i]^k
        矩阵乘法
        Sn = ∑F[i]^k = F[n]^k + Sn-1 = (aF[n-1]+bF[n-2])^k + Sn-1           二项式定理展开
           =  C(k,0)F[n-1]^kF[n-2]^0 +  + C(k,k)F[n-1]^0F[n-2]^k + Sn-1            *
        构造向量 
        T(n-1) = (F[n-1]^kF[n-2]^0 ,  , F[n-1]^0F[n-2]^k , Sn-1)
        则T(n) = (F[n]^kF[n-1]^0 ,  , F[n]^0F[n-1]^k , Sn)
        现在的问题是如何构造出矩阵A,使得  T(n-1)*A = T(n)
        由式子*,我们能知道A的最后一列(第K+1列)是(C(k,0) ,  , C(k,k),1)T
    
        现在剩下的问题是维护向量剩下的那些项,即F[n-1]^xF[n-2]^(k-x)
        同样是用二项式定理展开即可,这里不赘述了
    */
    
    #include<algorithm>
    #include<iostream>
    #include<math.h>
    using namespace std;
    
    const int N = 50+10;
    
    __int64 init[N][N],ret[N][N],temp[N][N],C[N][N];
    __int64 mod;
    __int64 xx[N];
    __int64 power(__int64 a,int k)
    {
        __int64 res=1;
        for(;k;k>>=1)
        {
            if(k&1) res=(res*a)%mod;
            a*=a;
            if(a>=mod) a%=mod;
        }
        return res;
    }
    
    void show(__int64 a[N][N],int n)
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
                cout<<a[i][j]<< ' ';
            cout<<endl;
        }
    }
    void init_mat(int x,__int64 a,__int64 b,__int64 f1,__int64 f2)
    {
        for(int i=0;i<=x;i++)
            C[i][i]=C[i][0]=1;
        for(int i=2;i<=x;i++)
            for(int j=1;j<i;j++)
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        memset(init,0,sizeof(init));
        for(int i=0;i<x+1;i++)
            for(int j=0;j<=x-i;j++)
                init[i][j]=(((C[x-i][j]*power(a,x-i-j))%mod)*power(b,j))%mod;
        for(int j=0;j<x+1;j++)
            init[x+1][j]=init[0][j];
        init[x+1][x+1]=1;
        for(int i=0;i<x+1;i++)
        {
            xx[i]=power(f2,x-i)*power(f1,i);
            if(xx[i]>=mod)
                xx[i]%=mod;
        }
        xx[x+1]=power(f1,x)+power(f2,x);
        if(xx[x+1]>=mod) xx[x+1]%=mod;
        //show(init,x+2);
    }
    
    void matmul(__int64 a[][N],__int64 b[][N],int n)
    {
        memset(temp,0,sizeof(temp));
        for(int i=0;i<n;i++)
            for(int k=0;k<n;k++)
                if(a[i][k])
                    for(int j=0;j<n;j++)
                        if(b[k][j])
                        {
                            temp[i][j]=temp[i][j]+a[i][k]*b[k][j];
                            if(temp[i][j]>=mod)
                                temp[i][j]%=mod;
                        }
        memcpy(a,temp,sizeof(temp));
    }
    
    void q_mod(int k,int n)
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                ret[i][j]=(i==j);
        for(;k;k>>=1)
        {
            if(k&1)
                matmul(ret,init,n);
            matmul(init,init,n);
        }
    }
    
    int main()
    {
        int n,k;
        int T;
        __int64 f1,f2,a,b;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%I64d %I64d %I64d %I64d %d %d %I64d",&f1,&f2,&a,&b,&k,&n,&mod);
            if(n==1)
            {
                printf("%I64d\n",power(f1,k));
                continue;
            }
            else if(n==2)
            {
                __int64 ans=power(f1,k)+power(f2,k);
                if(ans>=mod) ans%=mod;
                printf("%I64d\n",ans);
                continue;
            }
            init_mat(k,a,b,f1,f2);
            q_mod(n-2,k+2);
            //show(ret,k+2);
            __int64 ans=0;
            for(int i=0;i<k+2;i++)
                ans=(ans+ret[k+1][i]*xx[i])%mod;
            printf("%I64d\n",ans);
        }
        return 0;
    }


    以上部分图片来自《矩阵乘法题目总结》

  • 相关阅读:
    http协议及http协议和tcp协议的区别
    Fastcgi协议定义解释与说明
    web请求响应
    Linux网络编程:一个简单的正向代理服务器的实现
    Nginx-请求处理与响应
    Nginx事件管理机制-epoll
    负载均衡
    Nginx-进程模型
    [转载]NGINX原理分析 之 SLAB分配机制
    Nginx惊群处理
  • 原文地址:https://www.cnblogs.com/nanke/p/2420067.html
Copyright © 2011-2022 走看看