kuangbin专题十九矩阵总结

B - 233 Matrix
我们一列一列的看,可以发现每一列可以由前一列推出,而且题目给的行数特别少,也算是给了一个提示:以列作为递推的对象。

#include<bits/stdc++.h>
using namespace std;
const int maxn=15;
const int mod=10000007;
typedef long long ll;
struct M
{
    int n,m;
    ll a[maxn][maxn];
    M(int _n=0)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
    }
    M(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }
    void mem(int _n = 0)
    {
        n = m = _n, memset(a, 0, sizeof(a));
    }
    void mem(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }
    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1; k <= a.m; k++)
            for (int i = 1; i <= a.n; i++)
                for (int j = 1; j <= b.m; j++)
                    (c.a[i][j] += (a.a[i][k] * b.a[k][j])%mod)%=mod;
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1; i <= n; i++)a[i][i] = 1;
    }
    void out()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                cout<<a[i][j]<<' ';
            cout<<endl;
        }
    }
};

M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    while(num)
    {
        if(num%2)
            res=res*a;
        num/=2;
        a=a*a;
    }
    return res;
}

int a[maxn];
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
            a[0]=23;
        a[n+1]=3;
        int times=m;
        M it(n+2);
        for(int i=1;i<=n+2;i++)
            for(int j=1;j<=n+2;j++)
        {
            if(j==1)
            {
                if(i!=n+2)
                    it.a[i][j]=10;
            }
            else if(j==n+2)
                it.a[i][j]=1;
            else
            {
                if(i>=j&&i<n+2)
                    it.a[i][j]=1;
            }
        }
        //it.out();
        M res=quick_mul(times,it);
        //res.out();
        ll ans=0;
        for(int i=1;i<=n+2;i++)
        {
            (ans+=(res.a[n+1][i]*a[i-1])%mod)%=mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D - Krypton Number System
设dp[i][j] 为已经获得的分数为i,最后的数字为j。那么dp[i][j]=dp[i][j]+dp[i-(j-k)*(j-k)][k].
容易看出,这个式子的复杂度为score*base*base.所以我们需要将其进行优化。
优化的方式肯定是矩阵啦,毕竟在这个专题。我们可以发现一个score必定最少是从score-(base-1) * (base-1)得来的。所以我们可以以这个范围的分数作为递推的对象,而j是从0到base-1,那么矩阵的大小为 (base-1) * (base-1)*base.
具体的矩阵如下所示。

M=000000000001000000000000000000000100100000000000010000000000001000000000000100000000000010000000000001000000000000100010000000010101000000001010dp(0,0)dp(0,1)dp(0,2)dp(1,0)dp(1,1)dp(1,2)dp(2,0)dp(2,1)dp(2,2)dp(3,0)dp(3,1)dp(3,2)=dp(1,0)dp(1,1)dp(1,2)dp(2,0)dp(2,1)dp(2,2)dp(3,0)dp(3,1)dp(3,2)dp(4,0)dp(4,1)dp(4,2) M = [ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 ] [ d p ( 0 , 0 ) d p ( 0 , 1 ) d p ( 0 , 2 ) d p ( 1 , 0 ) d p ( 1 , 1 ) d p ( 1 , 2 ) d p ( 2 , 0 ) d p ( 2 , 1 ) d p ( 2 , 2 ) d p ( 3 , 0 ) d p ( 3 , 1 ) d p ( 3 , 2 ) ] = [ d p ( 1 , 0 ) d p ( 1 , 1 ) d p ( 1 , 2 ) d p ( 2 , 0 ) d p ( 2 , 1 ) d p ( 2 , 2 ) d p ( 3 , 0 ) d p ( 3 , 1 ) d p ( 3 , 2 ) d p ( 4 , 0 ) d p ( 4 , 1 ) d p ( 4 , 2 ) ]

#include<bits/stdc++.h>
using namespace std;
typedef unsigned int uint;
const int maxn=160;
struct M
{
    int n, m;
    uint a[maxn][maxn];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }

    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    c.a[i][j] += a.a[i][k] * b.a[k][j];
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }
    void out()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                cout<<a[i][j]<<' ';
            cout<<endl;
        }
    }
};

M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}

int base,score;
uint dp[30][10];
void pre_init()
{
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=base-1;i++)
        dp[0][i]=1;
    for(int i=1;i<=(base-1)*(base-1)-1;i++)
        for(int j=0;j<=base-1;j++)
    {
        for(int k=0;k<=base-1;k++)
        {
            if((j-k)*(j-k)>i||j==k)continue;
            dp[i][j]+=dp[i-(j-k)*(j-k)][k];
        }
    }


}

int main()
{
    int T;
    scanf("%d",&T);
    int cas=1;
    while(T--)
    {

        scanf("%d %d",&base,&score);
        int len=(base-1)*(base-1)*base;
        M a(len);
        int nowr=1,nowc=base+1;
        while(nowc<=len)
        {
            a.a[nowr][nowc]=1;
            nowr++,nowc++;
        }
        int val=(base-1)*(base-1);
        for(int i=0;i<base;i++,nowr++)
        {
            for(int j=0;j<base;j++)if(i!=j)
            {
                int bef=val-(i-j)*(i-j);
                int itc=bef*base+j+1;
                a.a[nowr][itc]=1;
            }
        }
        //a.out();
        pre_init();
        printf("Case %d: ",cas++);
        if(score<=(base-1)*(base-1)-1)
        {
            uint ans=0;
            for(int i=0;i<=base-1;i++)
                ans=ans+dp[score][i];
            cout<<ans<<endl;
        }
        else
        {
            int times=score-(base-1)*(base-1)+1;
            M res=quick_mul(times,a);
            uint ans=0;
            for(int i=0;i<=base-1;i++)
            {
                for(int j=1;j<=len;j++)
                {
                    int r=(j-1)/base;
                    int c=(j-1)%base;
                    ans=ans+res.a[len-base+i+1][j]*dp[r][c];
                }
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}

E - Fast Matrix Calculation
如果直接根据题意计算的话,最后的矩阵大小可达1000*1000,存都存不了。所以我们换种方式就好了。
我们要计算(A*B)^(N * N),其实该式等于
A*(B A)^(N *N-1) B(数学渣表示一开始根本没想到。)
那么现在的矩阵大小就变成了6*6的了。

#include<bits/stdc++.h>
using namespace std;
const int mod=6;
int A[1005][10],B[10][1005],C[1005][1005],ans[1005][1005];
struct M
{
    int n, m;
    int a[10][10];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }

    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    (c.a[i][j] += (a.a[i][k] * b.a[k][j])%mod)%=mod;
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }
    void out()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                cout<<a[i][j]<<' ';
            cout<<endl;
        }
    }
};

M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}



int main()
{
    int N,K;
    while(~scanf("%d %d",&N,&K)&&N)
    {
        memset(C,0,sizeof(C));
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=N;i++)
            for(int j=1;j<=K;j++)
            scanf("%d",&A[i][j]);
        for(int i=1;i<=K;i++)
            for(int j=1;j<=N;j++)
            scanf("%d",&B[i][j]);
        M c(K);
        for(int k=1;k<=N;k++)
            for(int i=1;i<=K;i++)
                for(int j=1;j<=K;j++)
                (c.a[i][j]+=B[i][k]*A[k][j]%mod)%=mod;
        //c.out();
        M tmp=quick_mul(N*N-1,c);
        for(int k=1;k<=K;k++)
            for(int i=1;i<=N;i++)
            for(int j=1;j<=K;j++)
            (C[i][j]+=A[i][k]*tmp.a[k][j]%mod)%=mod;
        for(int k=1;k<=K;k++)
            for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
            (ans[i][j]+=C[i][k]*B[k][j]%mod)%=mod;
        int res=0;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                res+=ans[i][j];
        printf("%d\n",res);
    }
    return 0;

}

H - Power of Matrix
如果直接计算的话那么复杂度为k*n^3,会爆炸,所以我们得想个方法进行优化。
S(k)=A+A2+A3+...+Ak S ( k ) = A + A 2 + A 3 + . . . + A k ,当k为偶数时, S(k)=(A+A2+A3+..+Ak/2)(1+Ak/2) S ( k ) = ( A + A 2 + A 3 + . . + A k / 2 ) ∗ ( 1 + A k / 2 ) ,同理k为奇数的时候也可以这样优化,那么复杂度变成了log(k)*n^3.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=45;
const int mod=10;
struct M
{
    int n, m;
    int a[maxn][maxn];
    M(int _n = 0)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
    }
    M(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }
    void mem(int _n = 0)
    {
        n = m = _n, memset(a, 0, sizeof(a));
    }
    void mem(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }

    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1; k <= a.m; k++)
            for (int i = 1; i <= a.n; i++)
                for (int j = 1; j <= b.m; j++)
                    (c.a[i][j] += (a.a[i][k] * b.a[k][j])%mod)%=mod;
        return c;
    }

    friend M operator+(M a, M b)
    {
        M c(a.n,a.m);
        for (int i = 1; i <= a.n; i++)
            for (int j = 1; j <= a.m; j++)
                c.a[i][j]=(a.a[i][j] + b.a[i][j])%mod;
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1; i <= n; i++)a[i][i] = 1;
    }
    void out()
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                printf("%d",a[i][j]%mod);
                if(j!=m)printf(" ");
                else puts("");
            }
        }
    }
};
M A;
int n,k;
M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}

M dfs(int num)
{
    if(num==1)return A;
    if(num%2)
    {
        M tmp=quick_mul(num,A);
        return tmp+dfs(num-1);
    }
    else
    {
        M tmp=quick_mul(num/2,A);
        M tmp2;
        tmp2.make_I(A.n);
        tmp=tmp+tmp2;
        return tmp*dfs(num/2);
    }

}

int main()
{

    while(~scanf("%d %d",&n,&k)&&n)
    {
        A.mem(n);
        ll tmp;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                {
                    scanf("%lld",&tmp);
                    A.a[i][j]=tmp%10;
                }
        M F(n);
        F=dfs(k);
        F.out();
        puts("");
    }
}

I - Contemplation! Algebra
S(n)=an+bn S ( n ) = a n + b n
我们可以用a+b,a*b找出S(n)的递推关系。找出关系后直接用矩阵快速幂即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5;
ll p,q,n;
struct M
{
    int n, m;
    ll a[maxn][maxn];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    c.a[i][j] += (a.a[i][k] * b.a[k][j]);
        return c;
    }

    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }

};

M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}
int main()
{
    while(scanf("%lld %lld %lld",&p,&q,&n)==3)
    {
        if(n==0)
        {
            puts("2");
            continue;
        }
        else if(n==1)
        {
            printf("%lld\n",p);
            continue;
        }

        M it(2);
        it.a[1][1]=p,it.a[1][2]=-q;
        it.a[2][1]=1;
        M res=quick_mul(n-2,it);
        ll ans=0;
        ll tmp=p*p-2*q;
        ans=res.a[1][1]*tmp+res.a[1][2]*p;
        printf("%lld\n",ans);
    }
    return 0;
}

J - Cellular Automaton
做的时候发现结构体的矩阵根本存不了,就将数组放外面,看时间给了18s,以为T不了,结果T了。看了题解才知道什么是循环矩阵,关于循环矩阵,博主会在下一篇博客说明。循环矩阵的计算只需要n^2,这样就不会T了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=505;
int N,M,D,K;
#define ceil cl
struct Mat
{
    ll arr[maxn];
};
Mat ceil,x;

Mat mul_mat(const Mat&a,const Mat&b)
{
    Mat ans;
    memset(ans.arr,0,sizeof(ans.arr));
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)
        ans.arr[i]=(ans.arr[i]+a.arr[j]*b.arr[(i-j+N)%N])%M;
    return ans;
}
void pow_mat(int n)
{
    Mat ans=x;
    while(n)
    {
        if(n&1)
            ans=mul_mat(ans,x);
        x=mul_mat(x,x);
        n>>=1;
    }
    x=ans;
}


int main()
{
    while(~scanf("%d %d %d %d",&N,&M,&D,&K))
    {
        for(int i=0;i<N;i++)
            scanf("%lld",&ceil.arr[i]);

        memset(x.arr,0,sizeof(x));
        for(int i=-D;i<=D;i++)
            x.arr[(i+N)%N]=1;
        pow_mat(K-1);
        for(int i=0;i<N;i++)
        {
            if(i)printf(" ");
            ll ret=0;
            for(int j=0;j<N;j++)
                ret=(ret+ceil.arr[j]*x.arr[(j-i+N)%N])%M;
            printf("%lld",ret);
        }
        puts("");
    }
    return 0;
}

M - So Easy!
数学渣表示根本不会做这个题。
我们要求 (a+b)n ( a + b ) n 向上取整的值,用脚趾头想一想如果直接快速幂的话,肯定会因为精度问题输出错误答案。所以应该怎么做呢?
这里有一个定义,就是共轭, (a+b)n ( a + b ) n (ab)n ( a − b ) n 相加必定是个整数。题目给的b的范围很有意思,在 (a1)2a2 ( a − 1 ) 2 到 a 2 之间,那么 ab a − b 必定是个小数,n次幂之后也只会是小数,所以我们实际要求的是 Sn=(a+b)n+(ab)n S n = ( a + b ) n + ( a − b ) n
到了现在,我们只需要求出 Sn S n 与它之前的递推式即可。这个留给读者啦。

#include<bits/stdc++.h>
using namespace std;
int mod;
struct M
{
    int n, m;
    int a[3][3];
    M(int _n = 0)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
    }
    M(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }
    void mem(int _n = 0)
    {
        n = m = _n, memset(a, 0, sizeof(a));
    }
    void mem(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }

    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1; k <= a.m; k++)
            for (int i = 1; i <= a.n; i++)
                for (int j = 1; j <= b.m; j++)
                    (c.a[i][j] += (1LL*a.a[i][k] * b.a[k][j])%mod)%=mod;
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1; i <= n; i++)a[i][i] = 1;
    }
};
M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}
int main()
{
    int a,b,n;
    while(cin>>a>>b>>n>>mod)
    {
        int ans;
        if(n==1)
            ans=2*a;
        else
        {
            M A(2);
            A.a[1][1]=2*a%mod;
            A.a[1][2]=(b-a*a)%mod;
            A.a[2][1]=1;
            int times=n-1;
            M res=quick_mul(times,A);
            int tmp=2*a%mod;
            ans=res.a[1][1]*tmp%mod+res.a[1][2]*2%mod;
        }
        printf("%d\n",(ans%mod+mod)%mod);
    }
}

N - Yet Another Number Sequence
这题第一次碰见肯定不会做。。太难了。
真正让我感觉到了数学之美。
我们一步一步的推吧。
An+1(k)=Fn+1(n+1)k A n + 1 ( k ) = F n + 1 ∗ ( n + 1 ) k
由牛顿二项式定理可得
(n+1)k=C0kn0+C1kn1+C2kn2+...+Ckknk=ki=0Cikni ( n + 1 ) k = C k 0 ∗ n 0 + C k 1 ∗ n 1 + C k 2 ∗ n 2 + . . . + C k k ∗ n k = ∑ i = 0 k C k i ∗ n i
所以可得
An+1(k)=Fn+1ki=0Cikni A n + 1 ( k ) = F n + 1 ∗ ∑ i = 0 k C k i ∗ n i
Fn+1=Fn+Fn1 F n + 1 = F n + F n − 1
所以 An+1(k)=Fnki=0Cikni+Fn1ki=0Cikni A n + 1 ( k ) = F n ∗ ∑ i = 0 k C k i ∗ n i + F n − 1 ∗ ∑ i = 0 k C k i ∗ n i
为了方便计算,我们设 u(n+1,k)=An+1(k),v(n+1,i)=Fn(n+1)i u ( n + 1 , k ) = A n + 1 ( k ) , v ( n + 1 , i ) = F n ∗ ( n + 1 ) i
那么原式可化为
u(n+1,k)=ki=0Ciku(n,i)+v(n,i)ki=0Cik u ( n + 1 , k ) = ∑ i = 0 k C k i ∗ u ( n , i ) + v ( n , i ) ∗ ∑ i = 0 k C k i
v(n+1,k)=Fn(n+1)i=Fnki=0Cikni=ki=0Ciku(n,i) v ( n + 1 , k ) = F n ∗ ( n + 1 ) i = F n ∗ ∑ i = 0 k C k i ∗ n i = ∑ i = 0 k C k i u ( n , i )
所以最终原式可化为
u(n+1,k)=ki=0Ciku(n,i)+v(n,i)ki=0Cik u ( n + 1 , k ) = ∑ i = 0 k C k i ∗ u ( n , i ) + v ( n , i ) ∑ i = 0 k C k i
v(n,k)=ki=0Ciku(n1,i) v ( n , k ) = ∑ i = 0 k C k i u ( n − 1 , i )
贴一个别人写好的矩阵。
这里写图片描述

#include<bits/stdc++.h>
using namespace std;
const int maxn=85;
const int mod=1e9+7;
typedef long long ll;
int C[45][45];
void init()
{
    C[0][0]=1;
    for(int i=1;i<=40;i++)C[i][0]=C[i][i]=1;
    for(int i=2;i<=40;i++)
        for(int j=1;j<i;j++)
    {
        C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        //cout<<C[i][j]<<endl;
    }
}

struct M
{
    int n, m;
    int a[maxn][maxn];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }

    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    (c.a[i][j] += (1LL*a.a[i][k] * b.a[k][j])%mod)%=mod;
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }

    void out()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                cout<<a[i][j]<<' ';
            cout<<endl;
        }
    }

};
M quick_mul(ll num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}
int main()
{
    init();
    //cout<<C[4][1]<<endl;
    ll n;
    int k;
    cin>>n>>k;
    M A(2*k+3);
    A.a[1][1]=1;
    int nowk=0;
    for(int i=2;i<=2*k+3;i++,nowk=(nowk+1)%(k+1))
        A.a[1][i]=C[k][nowk];
    int r=0;
    for(int i=2;i<=k+2;i++,r++)
    {
        for(int j=2;j-2<=r;j++)
        {
            A.a[i][j]=C[r][j-2];
            A.a[i][j+k+1]=C[r][j-2];
        }
    }
    r=0;
    for(int i=k+3;i<=2*k+3;i++,r++)
    {
        for(int j=2;j-2<=r;j++)
            A.a[i][j]=C[r][j-2];
    }
    //A.out();
    M B(2*k+3,1);
    for(int i=1;i<=k+2;i++)
        B.a[i][1]=1;
    for(int i=k+3;i<=2*k+3;i++)
        B.a[i][1]=1;
    ll times=n-1;
    M res=quick_mul(times,A);
    //res.out();
    M ans=res*B;
    cout<<ans.a[1][1]<<endl;
}

P - Construct a Matrix
这题的关键点在于如何构造矩阵。
观察可得当计算出来的矩阵边长为奇数时构造不出来。
所以只有偶数的时候才能构造。
构造方法为左上部分全部为1,右下部分全部为-1,中间对角线一半为0,一半为1。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
int mod;

struct M
{
    int n, m;
    int a[5][5];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    (c.a[i][j] += (a.a[i][k] * b.a[k][j])%mod)%=mod;
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }
};
M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}
int main()
{
    int T;
    scanf("%d",&T);
    M A(3);
    A.a[1][1]=A.a[1][2]=A.a[1][3]=A.a[2][2]=A.a[2][3]=A.a[3][2]=1;
    int cas=1;
    while(T--)
    {
        int L;
        int n,m;
        scanf("%d %d",&n,&m);
        printf("Case %d: ",cas++);
        mod=m;
        M res=quick_mul(n-2,A);
        L=0;
        L=((res.a[1][1]*2%mod+res.a[1][2])%mod+res.a[1][3])%mod;
        if(L%2||L==0)
            puts("No");
        else
        {
            puts("Yes");
            for(int i=1;i<=L;i++)
            {
                for(int j=1;j<=L;j++)
                {
                    if(i+j<L+1)
                    {
                        printf("1");
                    }
                    else if(i+j==L+1)
                    {
                        if(i<=L/2)
                            printf("0");
                        else
                            printf("1");
                    }
                    else
                        printf("-1");
                    if(j!=L)
                        printf(" ");
                }
                puts("");
            }
        }
    }
}

R - M斐波那契数列
分开计算a和b的指数,容易发现他们的指数就是斐波那契数列。
另外求出来的指数会很大,那该怎么办呢?
当模数为质数时,由费马小定理得 ap11(mod p) a p − 1 ≡ 1 ( m o d   p )
所以我们可以将其指数模上p-1.

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
const int mod=1000000007;
typedef long long ll;
struct M
{
    int n, m;
    ll a[3][3];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    (c.a[i][j] += (a.a[i][k] * b.a[k][j])%(mod-1))%=(mod-1);
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }

};
M quick_mul(int num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}

ll FM(int times,int num)
{
    ll ans=1;
    ll tmp=num;
    while(times)
    {
        if(times%2)ans=ans*tmp%mod;
        times/=2;
        tmp=tmp*tmp%mod;
    }
    return ans;
}

int main()
{
    int a,b,n;
    M A(2);
    A.a[1][1]=A.a[1][2]=A.a[2][1]=1;
    while(cin>>a>>b>>n)
    {
        if(n==0)
        {
            cout<<a<<endl;
        }
        else if(n==1)
            cout<<b<<endl;
        else
        {int times=n-1;
        M B=quick_mul(times,A);
        ll ans=0;
        ans=FM(B.a[1][2],a)*FM(B.a[1][1],b)%mod;
        cout<<ans<<endl;
        }
    }
    return 0;
}

S - Arc of Dream
aibi a i ∗ b i 拆开计算从而得到它的递推式,接着进行矩阵快速幂即可得到答案。
博主智障的把N传参到一个int类型的函数中,以至于一直wa,对拍发现大数据才会有问题,但看该取模的地方都取模了,所以找了一下午的bug。。最后才发现传参的时候爆了。。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll mod=1000000007;

struct M
{
    int n, m;
    ll a[10][10];
    M(int _n = 0)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
    }
    M(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }
    void mem(int _n = 0)
    {
        n = m = _n, memset(a, 0, sizeof(a));
    }
    void mem(int _n, int _m)
    {
        n = _n, m = _m;
        memset(a, 0, sizeof(a));
    }
    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1; k <= a.m; k++)
            for (int i = 1; i <= a.n; i++)
                for (int j = 1; j <= b.m; j++)
                    (c.a[i][j] += (a.a[i][k] * b.a[k][j])%mod)%=mod;
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1; i <= n; i++)a[i][i] = 1;
    }
    void out()
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
                cout<<a[i][j]<<' ';
            cout<<endl;
        }
    }

};
M quick_mul(ll num,M a)
{
    M res;
    res.make_I(a.n);
    M tmp=a;
    while(num)
    {
        if(num%2)res=res*tmp;
        num/=2;
        tmp=tmp*tmp;
    }
    return res;
}
M a;
ll N,A0,AX,AY,B0,BX,BY;
ll first[10];
void init()
{
    // N%=(mod-1);
    A0%=mod,AX%=mod,AY%=mod;
    B0%=mod,BX%=mod,BY%=mod;
    a.mem(6);
    a.a[1][1]=1;
    a.a[1][2]=a.a[2][2]=AX*BX%mod;
    a.a[1][3]=a.a[2][3]=AX*BY%mod;
    a.a[1][4]=a.a[2][4]=AY*BX%mod;
    a.a[1][5]=a.a[2][5]=BY;
    a.a[3][3]=AX,a.a[3][5]=1;
    a.a[4][4]=BX,a.a[4][6]=1;
    a.a[5][5]=1;
    a.a[6][6]=1;
    //a.out();
    //a=a*a;
    //a.out();
    first[1]=A0*B0%mod;
    first[2]=A0*B0%mod;
    first[3]=A0;
    first[4]=B0;
    first[5]=AY;
    first[6]=BY;
}

int main()
{

    //freopen("D:\\1.txt","r",stdin);
    //freopen("D:\\2.txt","w",stdout);
    while(~scanf("%lld",&N))
    {
        scanf("%lld %lld %lld %lld %lld %lld",&A0,&AX,&AY,&B0,&BX,&BY);
        init();
        if(N==0)
        {
            puts("0");
            continue;
        }

        ll times=N-1;
        M res=quick_mul(times,a);
        ll ans=0;
        for(int i=1; i<=6; i++)
        {
            ans=ans+res.a[1][i]*first[i]%mod;
            ans%=mod;
        }
        printf("%lld\n",ans);
    }
}

矩阵只是一个板子,最重要的是需要自己推出递推式。
最近写题老有bug,打比赛的时候都不敢交题,怕被罚时。经过本专题的bug洗礼之后,博主内心变得无比平静,罚时就罚时吧,QTMD!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值