矩阵快速幂在ACM中的应用

16计算机2黄睿博

首发于个人博客http://www.cnblogs.com/BobHuang/

作为一个acmer,矩阵在这个算法竞赛中还是蛮多的,一个优秀的算法可以影响到一个程序的运行速度的快慢,在算法竞赛中常常采用快速幂算法,因为有些递推式及有些问题都可以化为矩阵,所以矩阵快速幂也就有了意义。

首先引入快速幂:

我们要求a^b,那么其实b是可以拆成二进制的,该二进制数第i位的权为2^(i-1),

例如当b=11时  11的二进制是1011

11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1因此,我们将a¹¹转化为算 因为存在这一相等关系,所以将O(n)复杂度的算法降到了O(logn),代码如下

int poww(int x, int n)
{
int ans=;
while(n)
{
if(n&)ans=ans*x;
x=x*x;
n>>=;
}
return ans;
}

这里面用到了一些奇怪的运算,位运算,关于这个详见我的另一篇博客(>>相当于/2,&1相当于判断末位是否为1,比较接近计算机底层,取模还有除法运算常数比较大)

我的矩阵快速幂模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=;
int G;
struct MX
{
ll v[N][N];
void O()
{
memset(v,,sizeof v);
}
void E()
{
memset(v,,sizeof v);
for(int i=; i<G; i++)v[i][i]=;
}
void P()
{
for(int i=; i<G; i++)
for(int j=; j<G; j++)printf(j==G-?"%d\n":"%d ",v[i][j]);
}
MX operator+(const MX &b) const
{
MX c;
c.O();
for(int i=; i<G; i++)
for(int j=; j<G; j++)c.v[i][j]=v[i][j]+b.v[i][j];
return c;
}
MX operator*(const MX &b)const
{
MX c;
c.O();
for(int k=; k<G; k++)
for(int i=; i<G; i++)
if(v[i][k])for(int j=; j<G; j++)c.v[i][j]+=v[i][k]*b.v[k][j];
return c;
}
MX operator^(int p)const
{
MX y,x;
y.E(),memcpy(x.v,v,sizeof(v));
for(; p; x=x*x,p>>=)if(p&)y=y*x;
return y;
}
} a,ans;

目前c/c++最大支持__int128,也就是最大的有符号整数为2^127-1,数量级大概是1e38,不过常用的还是long long,是长整形,2^63-1。所以常用一个MOD质数的方法,这样得到的答案更加丰富。

引入完毕,接下来进入正题,无非是将以上的乘法转换为矩阵乘法。

我们从最简单的斐波那契数列入手,求菲波那切数列的第n项,n很大,所以MOD1e9+7

斐波的为前两项之和,即发f[0]=1,f[1]=1,f[i] = f[i-1]+f[i-2](i>1)

比较简单的可以直接得到递推式

,进一步递推

由于矩阵乘法满足结合律,所它也可以用快速幂算法求解。

代码如下

struct Matrix
{
long long a[][];
Matrix()
{
memset(a,,sizeof(a));
}
Matrix operator*(const Matrix y)
{
Matrix ans;
for(int i=; i<=; i++)
for(int j=; j<=; j++)
for(int k=; k<=; k++)
ans.a[i][j]+=a[i][k]*y.a[k][j];
for(int i=; i<=; i++)
for(int j=; j<=; j++)
ans.a[i][j]%=M;
return ans;
}
void operator=(const Matrix b)
{
for(int i=; i<=; i++)
for(int j=; j<=; j++)
a[i][j]=b.a[i][j];
}
};
long long solve(long long x)
{
Matrix ans,t;
ans.a[][]=ans.a[][]=;
t.a[][]=t.a[][]=t.a[][]=;
while(x)
{
if(x&)ans=ans*t;
t=t*t;
x>>=;
}
return ans.a[][];
}

强行递推式

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = ;
unordered_map<ll,ll> M;
ll fic(ll x)
{
if(M.count(x))return M[x];
ll a=(x+)/,b=x/+;
return M[x]=((fic(a)*fic(b))%mod+(fic(a-)*fic(b-)))%mod;
}
int main()
{
ll n;
M[]=,M[]=M[]=;
cin>>n;
cout<<fic(n);
return ;
}

如果经过推理得到一个递推式呢

如果对矩阵乘法还是不太懂的话,可以先看下这个知乎回答

例子HDU2842

Dumbear likes to play the Chinese Rings (Baguenaudier). It’s a game played with nine rings on a bar. The rules of this game are very simple: At first, the nine rings are all on the bar.

The first ring can be taken off or taken on with one step.

If the first k rings are all off and the (k + 1)th ring is on, then the (k + 2)th ring can be taken off or taken on with one step. (0 ≤ k ≤ 7)

Now consider a game with N (N ≤ 1,000,000,000) rings on a bar, Dumbear wants to make all the rings off the bar with least steps. But Dumbear is very dumb, so he wants you to help him.

九连环:如果要拆第n个环,那么第n-1个环就必须在竿上,

前n-2个环都必须已经被拆下,题目是现在是一个n连环,求将n连环全部

拆掉需要的最少的步骤%200907.

如果前k个环被拆掉,第k+1个还被挂着,那么第k+2个就可以拿下或者装上

f[n] = 2 * f[n - 2] + f[n - 1] + 1

就是f(n)=f[n-1]+2*f[n-2]+1,f[n-1]=1*f[n-1],1=1;

就是可以从前两个状态推到当前状态,这个关系矩阵的系数还是比较好找的

下一个例子

n个六边形排成一行,相邻两个六边形共用一条边,如下图所示:

记这个图形的生成树个数为t(n)(由于每条边都是不同的,不存在同构的问题)。那么t(1)=6,t(2)=35……

给出n,求 mod 1000000007

第i个矩形右边那条边会不会去掉的方案数

dp[i][1]=dp[i-1][0]+dp[i-1][1],

dp[i][0]=4*dp[i-1][1]+5*dp[i-1][0]

i个六边形总个数就是dp[i][0] + dp[i][1];

矩阵就是

分析下dp[i+1][2],这一项已经做了前缀和,所以和之前的总数是不一样的

dp[i+1][2]=dp[i+1][1]+dp[i+1][0]+dp[i][2]=dp[i][0]+dp[i][1]+4dp[i][0]+5dp[i-1][1]+dp[i][2]

=5dp[i][0]+6dp[i][1]+dp[i][2]

所以这个就很好解决了

假设a[i]为当前项的前缀和,很容易发现

a[i] =6*a[i-1] - a[i-2] +5

这个用矩阵怎么填呢

可以这样填,还有看到他们最后把a[0][0]-1的,但是相信懂了矩阵乘法的你懂这戏额都是在干嘛

(因为初值的设置不同)

 最后一个例子

一个n位数,它的每位都是奇数,且数字1和3出现偶数次,这样的n位数有多少个。比如说n=1,只有3个,它们分别是5,7和9。让你求下满足这些条件的数的个数MOD9973,对于给定的n都会包含有四种状态,1和3的个数都是奇数;1是奇数,3是偶数;1是偶数,3是奇数;1是偶数,3是偶数。

1是偶数,3是偶数是我想要的状态

在相互转换中其实是可以直接写出系数的

a[0][3]是我们要的状态即为所求

代码如下

#include <stdio.h>
#include <string.h>
const int MD=;
struct matrix
{
int mat[][];
};
matrix matmul(matrix a,matrix b,int n)
{
int i,j,k;
matrix c;
memset(c.mat,,sizeof(c.mat));
for(i=; i<n; i++)
{
for(j=; j<n; j++)
{
for(k=; k<n; k++)
{
c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%MD;
} }
}
return c;
}
matrix matpow(matrix a,int k,int n)
{
matrix b;
int i;
memset(b.mat,,sizeof(b.mat));
for(i=; i<n; i++) b.mat[i][i]=;
while(k)
{
if(k&) b=matmul(a,b,n);
a=matmul(a,a,n);
k>>=;
}
return b;
}
int main()
{
int k;
while(~scanf("%d",&k))
{
matrix a,b,ans;
memset(a.mat,,sizeof(a.mat));
memset(b.mat,,sizeof(b.mat));
a.mat[][]=;
for(int i=; i<; i++)
{
for(int j=; j<; j++)
{
if(i==j) b.mat[i][j]=;
else if(i+j==) b.mat[i][j]=;
else b.mat[i][j]=;
}
}
ans=matmul(a,matpow(b,k,),);
printf("%d\n",ans.mat[][]);
}
return ;
}

矩阵快速幂在ACM中的应用的更多相关文章

  1. 2020牛客寒假算法基础集训营1 J. 缪斯的影响力 (矩阵快速幂/费马小定理降幂)

    https://ac.nowcoder.com/acm/problem/200658 f(n) = f(n-1) * f(n-2) * ab ,f的第一项是x,第二项是y. 试着推出第三项是x·y·a ...

  2. HDU 3802 矩阵快速幂 化简递推式子 加一点点二次剩余知识

    求$G(a,b,n,p) = (a^{\frac {p-1}{2}}+1)(b^{\frac{p-1}{2}}+1)[(\sqrt{a} + \sqrt{b})^{2F_n} + (\sqrt{a} ...

  3. UOJ424 Count 生成函数、多项式求逆、矩阵快速幂

    传送门 两个序列相同当且仅当它们的笛卡尔树相同,于是变成笛卡尔树计数. 然后注意到每一个点的权值一定会比其左儿子的权值大,所以笛卡尔树上还不能够存在一条从根到某个节点的路径满足向左走的次数\(> ...

  4. POJ 2778 DNA Sequence ( AC自动机、Trie图、矩阵快速幂、DP )

    题意 : 给出一些病毒串,问你由ATGC构成的长度为 n 且不包含这些病毒串的个数有多少个 分析 : 这题搞了我真特么久啊,首先你需要知道的前置技能包括 AC自动机.构建Trie图.矩阵快速幂,其中矩 ...

  5. 计蒜客 ACM训练联盟周赛 第一场 Alice和Bob的Nim游戏 矩阵快速幂

    题目描述 众所周知,Alice和Bob非常喜欢博弈,而且Alice永远是先手,Bob永远是后手. Alice和Bob面前有3堆石子,Alice和Bob每次轮流拿某堆石子中的若干个石子(不可以是0个), ...

  6. 华东交通大学2018年ACM“双基”程序设计竞赛 C. 公式题 (2) (矩阵快速幂)

    题目链接:公式题 (2) 比赛链接:华东交通大学2018年ACM"双基"程序设计竞赛 题目描述 令f(n)=2f(n-1)+3f(n-2)+n,f(1)=1,f(2)=2 令g(n ...

  7. [技术]浅谈OI中矩阵快速幂的用法

    前言 矩阵是高等代数学中的常见工具,也常见于统计分析等应用数学学科中,矩阵的运算是数值分析领域的重要问题. 基本介绍 (该部分为入门向,非入门选手可以跳过) 由 m行n列元素排列成的矩形阵列.矩阵里的 ...

  8. 2017 ECJTU ACM程序设计竞赛 矩阵快速幂+二分

    矩阵 Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other) Total Submission ...

  9. 2017 ACM/ICPC Asia Regional Shenyang Online:number number number hdu 6198【矩阵快速幂】

    Problem Description We define a sequence F: ⋅ F0=0,F1=1;⋅ Fn=Fn−1+Fn−2 (n≥2). Give you an integer k, ...

随机推荐

  1. SPOJ BALNUM Balanced Numbers 平衡数(数位DP,状压)

    题意: 平衡树定义为“一个整数的某个数位若是奇数,则该奇数必定出现偶数次:偶数位则必须出现奇数次”,比如 222,数位为偶数2,共出现3次,是奇数次,所以合法.给一个区间[L,R],问有多少个平衡数? ...

  2. Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available

    java.lang.IllegalStateException: Failed to load ApplicationContext    at org.springframework.test.co ...

  3. BOM属性对象方法

    本文原链接:https://cloud.tencent.com/developer/article/1018747 BOM 1.window对象 2.location对象 3.history对象 BO ...

  4. python之道11

    day11作业 请写出下列代码的执行结果: 例一: def func1(): print(**'in func1'**) def func2(): print(**'in func2'**) ret ...

  5. 前缀树,trie树

    前缀树: 假设一个字符串数组,“abcd”,"bcd","gef" , 构建一颗树,字母是在路径上,节点上最基本的存储的信息包括: 以这个节点结尾的 字符串的数 ...

  6. vue入坑教程(三)vue父子组件之间互相调用方法以及互相传递数据

    1.父组件调用子组件的方法 父组件: <template> <div> <button v-on:click="clickParent">点击& ...

  7. tcp 高性能服务, netty,mqtt

    1. io 线程不要有比较长的服务. 全部异步化. [1] netty 权威指南上只是说业务复杂时派发到业务线程池种. 共用的线程池最好都轻量. 多层线程池后, 下层的可以进行隔离. 这个是 mqtt ...

  8. SVN:The working copy is locked due to a previous error (一)

    使用 Cornerstone  时,碰到如题问题,SVN无法Update.Commit等操作. 解决办法:Working Copies ⟹ '右键' ⟹ Clean 即可解决! 尊重作者劳动成果,转载 ...

  9. 【点分治】luoguP2664 树上游戏

    应该是一道中等难度的点分?麻烦在一些细节. 题目描述 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 现在他想让你求出所有的sum[i] ...

  10. 【android】安卓开发apk列表

    - 谷歌的Zxing框架的扫码软件 (目前国内的应用商店很少此种类型的扫码app) - 解析IP地址功能,从IP地址(子网掩码)自动解析出网段,广播地址