矩阵乘法学习笔记(二)(vijos1049+vijos1067)

https://www.vijos.org/p/1049

题目大概是一个初始数列 1 2 3……
有m个操作,每次将原数列的第a[i]个值移动到i这个位置(m <= <script type="math/tex" id="MathJax-Element-114"><=</script>10)
共进行k次操作,求最终序列。(k为INT_MAX)

这也是一个很巧妙的运用
对于一个排列与一个操作,可以用矩阵乘法来快速变化。
举题目中的例子,假设此时数列A[]=6 1 3 7 5 2 4,而操作为B[]=3 2 4 5 6 7 1
那么我们将操作B[]变成如下的矩阵7 7矩阵
0010000
0100000
0001000
0000100
0000010
0000001
1000000
与71矩阵
6
1
3
7
5
2
4
相乘得出的便是变换后的数列,原理也很简单C[i][j]=B[i][k] A[k][j],我们将B矩阵看成一个bool数组,那么对于C[i][1]而言,只有i为操作的位置时,才会取得A[1][j]的值,语文学的不太好,但实际上很好理解。

然后对于这个非常大的k值,我们可以将m次操作为一次循环,直接求出一整次循环的等价操作,然后用快速幂将所有这些操作全部变为一个等价操作。最后假如m不整除k,余数直接暴力模拟即可。
代码看看就好,毕竟是真的丑

#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=1010;
const int INF=1e9+7;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
    char ls;ll x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------------------------------*/
ll n,m,k;
struct kaga
{
    ll r,c;
    ll v[110][110];
    kaga friend operator *(kaga a,kaga b)
    {
        kaga c;
        c.r=a.r;c.c=b.c;
        fer(i,1,c.r)
            fer(j,1,c.c)
            {
                c.v[i][j]=0;
                fer(k,1,a.c)
                c.v[i][j]+=a.v[i][k]*b.v[k][j]; 
            }
        return c; 
    }
    void friend print(kaga a)
    {
        cout<<a.r<<" "<<a.c<<endl;
        fer(i,1,a.r)
        {
            fer(j,1,a.c)
            cout<<a.v[i][j]<<" ";
            cout<<endl; 
        }
    }
    kaga friend operator ^(kaga a,ll k)
    {
        kaga b;
        b.r=a.r;b.c=a.c;
        fer(i,1,a.r)
            fer(j,1,a.c)
                if(i==j)b.v[i][j]=1;
                else b.v[i][j]=0;
        for(;k;k>>=1,a=a*a)
            if(k&1)b=b*a;
        return b;   
    }
}a[110],b,c;
int main()
{
    m=read();n=read();k=read();
    memset(a,0,sizeof(a));
    fer(i,1,n)a[i].r=a[i].c=m;
    fer(i,1,n)
        fer(j,1,m)
        {
            int x=read();
            a[i].v[j][x]=1; 
        }
    b.r=m;b.c=1;
    fer(i,1,m)b.v[i][1]=i;
    fer(i,1,n)b=a[i]*b;
    int t1=k/n,t2=k%n;
    c.r=m;c.c=m;
    fer(i,1,m)c.v[i][b.v[i][1]]=1;
    fer(i,1,m)b.v[i][1]=i;
    c=c^t1;
    b=c*b;
    fer(i,1,t2)b=a[i]*b;
    fer(i,1,m)cout<<b.v[i][1]<<" ";
}



https://www.vijos.org/p/1067

题目大意很好理解,推出来的dp方程为f[i]=i1j=ikf[j]
顺便,这道题n的范围是INT_MAX
稍微想一想会发现其实这道题的dp方程与斐波那契公式十分的相似
其实这道题做法就是斐波那契那道题的推广
矩阵A[]为
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 0
1 1 1 1 1 1
而矩阵B[]为
F[n-4]
F[n-3]
F[n-2]
F[n-1]
F[n]
那么相乘之后就得到矩阵
F[n-3]
F[n-2]
F[n-1]
F[n]
F[n+1]
是不是和斐波那契那道题几乎一模一样
用markdown画矩阵太蛋疼了,还是直接上代码(真丑)

#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=1010;
const int INF=1e9+7;
const int mod=7777777;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
    char ls;ll x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------------------------------*/
ll f[maxn],n,m;
struct kaga
{
    ll v[11][11];
    ll r,c;
    kaga friend operator *(kaga a,kaga b)
    {
        kaga c;
        c.r=a.r;c.c=b.c;
        fer(i,1,c.r)
            fer(j,1,c.c)
            {
                c.v[i][j]=0;
                fer(k,1,a.c)
                c.v[i][j]=(c.v[i][j]+(a.v[i][k]*b.v[k][j])%mod+mod)%mod;    
            }
        return c;
    }
    void friend print(kaga a)
    {
        fer(i,1,a.r)
        {
            fer(j,1,a.c)
            cout<<a.v[i][j]<<" ";
            cout<<endl; 
        }
        return ;
    }
    kaga friend operator ^(kaga a,ll k)
    {
        kaga b;
        b.r=a.r;b.c=a.c;
        fer(i,1,m)
            fer(j,1,m)
                if(i==j)b.v[i][j]=1;
                else b.v[i][j]=0;
        for(;k;k>>=1,a=a*a)
            if(k&1)b=b*a;
        return b;   
    }
}a,b;
int main()
{
    m=read();n=read();
    f[0]=1;
    fer(i,1,m+1)
        fer(j,1,m)
        if(i-j>=0)
        f[i]+=f[i-j];
    fer(i,1,m-1)
        fer(j,1,m)
            if(j==i+1)a.v[i][j]=1;
    fer(i,1,m)a.v[m][i]=1;
    a.r=a.c=m;
    b.r=m;b.c=1;
    fer(i,1,m)b.v[i][1]=f[i];
    if(n<=m+1)
    {
        cout<<f[n]<<endl;
        return 0;
    }
    a=a^(n-m);
    a=a*b;
    cout<<a.v[m][1];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值