矩阵快速幂专题(二)

来源:互联网 发布:gta5n卡掉帧如何优化 编辑:程序博客网 时间:2024/05/22 13:47

久等了,第二弹来了,这次的八道题大部分都非常简单,但是最后一题特别坑爹,不太好想到而且有个让人吐血的坑点。建议以后刷Light oj的朋友们做好心理准备,那上面的题目都非常坑,我以前做过一道最小生成树,就TLE了我整整一下午,没想到这次一道矩阵快速幂又是一下午,唉~~~,心累。


第一题  zoj-3690

分析:拿到这道题,我考虑第n位的数字是什么(假设前n位都满足规则),对于第n位的数字,考虑我可以在后面n+1位上放哪些数字。当第n位数字小于等于k时,可以在后面放与第n位不相同的同样小于等于k的数字,有k-1种,同时转化为最后一位是小于等于k的情况;还可以在后面放大于k的数字,有n-k种,转化为最后一位是大于k的情况。当第n位数字大于k的情况,可以放k个小于等于k的数字,转化为最后一位小于等于k的情况;还可以放大于k的n-k个数,转化为最后一位大于k的情况。

那么我就假设,对于n个数,满足规则的且最后一位大于k的情况有A(n)种,满足情况且最后一位小于等于k的情况有B(n)种。那么就有,A(n+1)=(n-k)*A(n)+(n-k)*B(n),B(n+1)=k*A(n)+(k-1)*B(n)

注意坑点:k可以等于0,那么不适合用矩阵解(k-1<0), 此时就是求一个整数快速幂,特判一下!

构造矩阵


<span style="font-size:18px;">#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 0+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = 1e9+7;const double eps = 1e-10;struct matrix {int n;ll maze[maxn][maxn];void init(int n){this->n=n;clr(maze,0);}matrix operator * (const matrix& rhs){matrix ans;ans.init(n);for(int i=0;i<n;i++)for(int j=0;j<n;j++)for(int k=0;k<n;k++)ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%mod;return ans;}};matrix qlow(matrix a,ll n){matrix ans;ans.init(a.n);for(int i=0;i<a.n;i++)ans.maze[i][i]=1;while(n){if(n&1)ans=ans*a;a=a*a;n>>=1;}return ans;}ll qpow(int b,ll n){ll ans=1;ll a=b;while(n){if(n&1)ans=ans*a%mod;a=a*a%mod;n>>=1;}return ans;}int main(){    //freopen("d:\\acm\\in.in","r",stdin);ll n;int m,k;while(~scanf("%lld %d %d",&n,&m,&k)){if(k==0){printf("%lld\n",qpow(m,n));continue;}matrix ans;ans.init(2);ans.maze[0][0]=(m-k)%mod;ans.maze[0][1]=k%mod;matrix ant;ant.init(2);ant.maze[0][0]=ant.maze[1][0]=(m-k)%mod;ant.maze[0][1]=k%mod;ant.maze[1][1]=(k-1)%mod;ant=qlow(ant,n-1);ans=ans*ant;printf("%lld\n",(ans.maze[0][0]+ans.maze[0][1])%mod);}    return 0;}</span>




第二题 Fzu-1683

分析:没什么好分析,裸的矩阵题。递推关系已经给你了,矩阵应该很简单就可以构造了。所以,直接上代码!

<span style="font-size:18px;">#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 0+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = 2009;//1e9+7;const double eps = 1e-10;struct matrix {int n;ll maze[maxn][maxn];void init(int n){this->n=n;clr(maze,0);}matrix operator * (const matrix& rhs){matrix ans;ans.init(n);for(int i=0;i<n;i++)for(int j=0;j<n;j++)for(int k=0;k<n;k++)ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%mod;return ans;}};matrix qlow(matrix a,int n){matrix ans;ans.init(a.n);for(int i=0;i<a.n;i++)ans.maze[i][i]=1;while(n){if(n&1)ans=ans*a;a=a*a;n>>=1;}return ans;}int main(){    //freopen("d:\\acm\\in.in","r",stdin);int m;scanf("%d",&m);for(int cas=1;cas<=m;cas++){int n;scanf("%d",&n);printf("Case %d: ",cas);if(n<=2){if(n==0)puts("1");else if(n==1)puts("4");else puts("9");continue;}matrix ans;ans.init(4);ans.maze[0][0]=5;ans.maze[0][1]=3;ans.maze[0][2]=1;ans.maze[0][3]=9;matrix ant;ant.init(4);ant.maze[0][0]=ant.maze[0][3]=3;ant.maze[1][0]=ant.maze[1][3]=2;ant.maze[2][0]=ant.maze[2][3]=7;ant.maze[0][1]=ant.maze[1][2]=ant.maze[3][3]=1;ant=qlow(ant,n-2);ans=ans*ant;printf("%lld\n",ans.maze[0][3]);}    return 0;}</span>




第三题 hdu-3306

分析:直接考虑S(n+1)和S(n)的关系,很快发现S(n+1)=S(n)+A(n+1)^2。再考虑A(n+1)^2的递推,联系题目给出的式子,可以将A(n+1)拆掉,得到A(n+1)^2=[X*A(n)+Y*A(n-1)]^2。

展开得到,A(n+1)^2=X^2*A(n)^2+Y^2*A(n-1)^2+2*X*Y*A(n)*A(n-1)。然后,发现A(n)^2和A(n-1)^2都可以放在矩阵里,但是A(n)*A(n-1)要放在矩阵里面需要考虑递推,A(n+1)*A(n)=[X*A(n)+Y*A(n-1)]*A(n)=X*A(n)^2+Y*A(n)*A(n-1)

构造矩阵


<span style="font-size:18px;">#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 0+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = 10007;//1e9+7;const double eps = 1e-10;struct matrix {    int n;    ll maze[maxn][maxn];    void init(int n)    {        this->n=n;        clr(maze,0);    }    matrix operator *(const matrix& rhs)    {        matrix ans;        ans.init(n);        for(int i=0;i<n;i++)            for(int j=0;j<n;j++)                for(int k=0;k<n;k++)                    ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%mod;        return ans;    }};matrix qlow(matrix a,int n){    matrix ans;    ans.init(a.n);    for(int i=0;i<a.n;i++)ans.maze[i][i]=1;    while(n)    {        if(n&1)ans=ans*a;        a=a*a;        n>>=1;    }    return ans;}int main(){    //freopen("d:\\acm\\in.in","r",stdin);    ll n,x,y;    while(~scanf("%lld %lld %lld",&n,&x,&y))    {        matrix ans;        ans.init(4);        ans.maze[0][0]=ans.maze[0][1]=ans.maze[0][2]=1;        ans.maze[0][3]=2;        matrix ant;        ant.init(4);        ant.maze[0][0]=y%mod;        ant.maze[1][0]=x%mod;        ant.maze[0][1]=ant.maze[0][3]=2*x*y%mod;        ant.maze[1][1]=ant.maze[1][3]=x*x%mod;        ant.maze[2][1]=ant.maze[2][3]=y*y%mod;        ant.maze[1][2]=ant.maze[3][3]=1;        ant=qlow(ant,n-1);        ans=ans*ant;        printf("%lld\n",ans.maze[0][3]);    }    return 0;}</span>




第四题 Uestc-1335

分析:裸的矩阵快速幂,入门题,敲着玩玩把!

啊呀,uestc oj似乎爆炸了(可能是暂时的),这题这么水,代码就不用了吧




第五题  poj-3233

分析:这道题有一种二分解法,但是这里我就不介绍了,我说下我自己的写法。

我还是把S和A分开来,S(n+1)=S(n)+A^(n+1),A^(n+1)=A^(n)*A,选择一个复合矩阵(好吧,其实叫分块矩阵),好像光看运行时间,并不快,但是这种做法简单啊!

构造矩阵


<span style="font-size:18px;">#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 60+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = 1e9+7;const double eps = 1e-10;int m;struct matrix {int n;ll maze[maxn][maxn];void init(int n){this->n=n;clr(maze,0);}matrix operator *(const matrix& rhs){matrix ans;ans.init(n);for(int i=0;i<n;i++)for(int j=0;j<n;j++)for(int k=0;k<n;k++)ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%m;return ans;}};matrix qlow(matrix a,int n){matrix ans;ans.init(a.n);for(int i=0;i<a.n;i++)ans.maze[i][i]=1;while(n){if(n&1)ans=ans*a;a=a*a;n>>=1;}return ans;}int main(){    //freopen("d:\\acm\\in.in","r",stdin);int n,k;scanf("%d %d %d",&n,&k,&m);matrix ans;ans.init(n<<1);for(int i=0;i<n;i++)for(int j=0;j<n;j++){scanf("%lld",&ans.maze[i][j]);ans.maze[i][n+j]=ans.maze[i][j];}for(int i=0;i<n;i++)ans.maze[i+n][i+n]=1;ans=qlow(ans,k);int flag;for(int i=0;i<n;i++){flag=0;for(int j=0;j<n;j++){if(flag)putchar(' ');flag=1;printf("%d",ans.maze[i][j+n]);}puts("");}    return 0;}</span>



第六题 hdu-2256

分析:和专题一里面的第四题是同一种题型,而且结果也大致相同,但是身为一个负责任的博主,还是会再写一遍的,毕竟这道题具有一般性,做完这道题这类题目就都不怕了。


<span style="font-size:18px;">#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 0+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = 1e9+7;const double eps = 1e-10;int m;struct matrix {    int n;    ll maze[maxn][maxn];    void init(int n)    {        this->n=n;        clr(maze,0);    }    matrix operator * (const matrix& rhs)    {        matrix ans;        ans.init(n);        for(int i=0;i<n;i++)            for(int j=0;j<n;j++)                for(int k=0;k<n;k++)                    ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%m;        return ans;    }};matrix qlow(matrix a,int n){    matrix ans;    ans.init(a.n);    for(int i=0;i<a.n;i++)ans.maze[i][i]=1;    while(n)    {        if(n&1)ans=ans*a;        a=a*a;        n>>=1;    }    return ans;}int main(){    //freopen("d:\\acm\\in.in","r",stdin);    int a,b,n;    while(~scanf("%d %d %d %d",&a,&b,&n,&m))    {        matrix ans;        ans.init(2);        ans.maze[0][0]=a%m;        ans.maze[0][1]=1;        ans.maze[1][0]=b%m;        ans.maze[1][1]=a%m;        ans=qlow(ans,n);        printf("%lld\n",ans.maze[0][0]*2%m);    }    return 0;}</span>



第七题 Uva-10870

分析:除了d是变化的以外,感觉这道题和斐波那契没什么区别。没啥好说的把,直接上图和代码。


<span style="font-size:18px;">#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 15+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = 1e9+7;const double eps = 1e-10;int m;int dat[maxn];int anw[maxn];struct matrix{int n;ll maze[maxn][maxn];void init(int n){this->n=n;clr(maze,0);}matrix operator * (const matrix& rhs){matrix ans;ans.init(n);for(int i=0;i<n;i++)for(int j=0;j<n;j++)for(int k=0;k<n;k++)ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%m;return ans;}};matrix qlow(matrix a,ll n){matrix ans;ans.init(a.n);for(int i=0;i<a.n;i++)ans.maze[i][i]=1;while(n){if(n&1)ans=ans*a;a=a*a;n>>=1;}return ans;}int main(){    //freopen("d:\\acm\\in.in","r",stdin);int d;ll n;while(scanf("%d %lld %d",&d,&n,&m),d||n||m){for(int i=0;i<d;i++)scanf("%d",&anw[i]);for(int i=0;i<d;i++)scanf("%d",&dat[i]);if(n<=d){printf("%d\n",dat[n-1]%m);continue;}matrix ans;ans.init(d);for(int i=0;i<d;i++)ans.maze[0][i]=dat[i];matrix ant;ant.init(d);for(int i=0;i<d-1;i++)ant.maze[i+1][i]=1;for(int i=0;i<d;i++)ant.maze[i][d-1]=anw[d-1-i];ant=qlow(ant,n-d);ans=ans*ant;printf("%lld\n",ans.maze[0][d-1]);}    return 0;}</span>



第八题 Light oj 1132

分析:这道比较难,我来好好分析一下。

首先,看到题目表示一脸懵逼。但是冷静下来,先认定一个事实,K是给你的常数而且非常小,不应该作为递推的量度(那么就应该是N作为递推的量度)。不妨设S(n)=1^K+2^K+......+N^K。那么,S(n+1)=S(n)+(N+1)^K,S(n)可以放到矩阵里面作为一项,但是(N+1)^K怎么办呢?我们考虑二项式定理!

(N+1)^K=C(K,0)*N^0+C(K,1)*N^1+C(K,2)*N^2+......+C(K,i)*N^i+......+C(K,K)*N^K

那么(N+1)^K就转化为了N^0、N^1、N^2、......、N^K,那么我们考虑不如全放到矩阵里面(反正最多只有50个),递推式的话还是使用二项式定理!

注意:

1、对于50之内的组合数的话,可以用公式C(n,k)=C(n-1,k)+C(n-1,k-1)打表打出来

2、这里有个大坑点,如果你直接用矩阵连着乘n次的话,中间数据就会爆long long,这时你需要一个类似快速幂的乘法,但是你真的这么写的话会超时,我看了别人的代码,才知道人家都是只算n-1次(奇迹的不会爆掉,卡的真好),然后最后一次是自己在外面写的,真是TM机智。同时我还要问候一下出题人的女性亲友,为什么不关爱一下出题人,让他有机会出这么反人类的题目。(当然也可能是我蠢,矩阵快速幂的姿势不好把,但是讲道理我已经优化了一下午了)

3、最大的坑点就是我的亲学长给我的矩阵快速幂模版非常的烂(听说他们私下里称这个模版为傻逼TLE矩阵快速幂模版,已经在多道题目上TLE了),然后我就崩溃了,在网上找到大神的模版后,我还不忘把他给我所有的模版通通删掉,准备有时间自己去找一波=、=。

构造矩阵


#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 50+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = (ll)1<<32;//1e9+7;const double eps = 1e-10; int siz;ll C[maxn][maxn];ll mul(ll a,ll b){    ll ans=0;    while(b)    {        if(b&1)ans=(ans+a)%mod;        a=(a+a)%mod;        b>>=1;    }    return ans;}void infi(){    for(int i=0;i<=50;i++)    {        C[i][0]=1;        C[i][i]=1;    }    for(int i=2;i<=50;i++)        for(int j=1;j<i;j++)            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;}struct matrix{    ll maze[maxn][maxn];    matrix()    {        clr(maze,0);        for(int i=0;i<siz;i++)            maze[i][i]=1;    }};matrix operator * (const matrix& A,const matrix& B){    matrix ans;    for(int i=0;i<siz;i++)        for(int j=0;j<siz;j++)        {            ans.maze[i][j]=0;            for(int k=0;k<siz;k++)                ans.maze[i][j]=(ans.maze[i][j]+A.maze[i][k]*B.maze[k][j]%mod)%mod;        }    return ans;}matrix qlow(matrix a,ll n){    matrix ans;    while(n)    {        if(n&1)ans=ans*a;        a=a*a;        n>>=1;    }    return ans;}int main(){    //freopen("d:\\acm\\in.in","r",stdin);    int t;    scanf("%d",&t);    infi();    for(int cas=1;cas<=t;cas++)    {        ll n;        int k;        scanf("%lld %d",&n,&k);        siz=k+2;        matrix ans;        clr(ans.maze,0);        for(int i=0;i<=k;i++)            for(int j=0;j<=i;j++)                ans.maze[j][i]=C[i][j];        for(int i=0;i<=k;i++)            ans.maze[i][k+1]=C[k][i];        ans.maze[k+1][k+1]=1;        ans=qlow(ans,n-1);        ll anw=0;        for(int i=0;i<=k+1;i++)            anw=(anw+ans.maze[i][k+1])%mod;        printf("Case %d: %lld\n",cas,anw);    }    return 0;}




小结:刷题之余,我和同学交流了一下,才发现矩阵快速幂和DP有点像,同样是求递推、状态转移。在DP数据比较大,不可以记忆画搜索的时候,就可以使用矩阵快速幂,给出答案。虽然没有做到这类题目,但是有种顿悟的感觉,蛮开心的!

0 0
原创粉丝点击