矩阵总结 ACM
先介绍一篇矩阵好的博文 Matrix 67:http://www.matrix67.com/blog/archives/276
一.高斯消元
我觉得不错的模板
// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解, //-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数) //有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var. int Gauss(int equ,int var){ int i,j,k; int max_r;// 当前这列绝对值最大的行. int col;//当前处理的列 int ta,tb; int LCM; int temp; int free_x_num; int free_index; for(int i=0;i<=var;i++){ x[i]=0; free_x[i]=true; } //转换为阶梯阵. col=0; // 当前处理的列 for(k = 0;k < equ && col < var;k++,col++){// 枚举当前处理的行. // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差) max_r=k; for(i=k+1;i<equ;i++) if(abs(a[i][col])>abs(a[max_r][col])) max_r=i; if(max_r!=k)// 与第k行交换 for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]); if(a[k][col]==0){// 说明该col列第k行以下全是0了,则处理当前行的下一列. k--; continue; } for(i=k+1;i<equ;i++){// 枚举要删去的行. if(a[i][col]!=0){ LCM = lcm(abs(a[i][col]),abs(a[k][col])); ta = LCM/abs(a[i][col]); tb = LCM/abs(a[k][col]); if(a[i][col]*a[k][col]<0)tb=-tb;//异号的情况是相加 for(j=col;j<var+1;j++) a[i][j] = a[i][j]*ta-a[k][j]*tb; } } } // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0). // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换. for (i = k; i < equ; i++) if (a[i][col] != 0) return -1; // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵. // 且出现的行数即为自由变元的个数. if (k < var){ // 首先,自由变元有var - k个,即不确定的变元至少有var - k个. for (i = k - 1; i >= 0; i--){ // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行. // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的. free_x_num = 0; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元. for (j = 0; j < var; j++) if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j; if (free_x_num > 1) continue; // 无法求解出确定的变元. // 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的. temp = a[i][var]; for (j = 0; j < var; j++) if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j]; x[free_index] = temp / a[i][free_index]; // 求出该变元. free_x[free_index] = 0; // 该变元是确定的. } return var - k; // 自由变元有var - k个. } // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵. // 计算出Xn-1, Xn-2 ... X0. for (i = var - 1; i >= 0; i--){ temp = a[i][var]; for (j = i + 1; j < var; j++) if (a[i][j] != 0) temp -= a[i][j] * x[j]; if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解. x[i] = temp / a[i][i]; } return 0; }
1.POJ 1222 EXTENDED LIGHTS OUT
题目:关灯问题,按下开关,该灯和相邻的灯都会变化灯的状态。现在给出初始状态,问如何设置开关,使得灯的最终状态全为关闭的。。
分析:我们发现对于i灯,必有ai ^ xi ^ (A) = 0,相当于xi ^ (A) = ai
A为相邻的几个变量xj ^ xk ^ xl & xm(假设有四个)
所以我们可以列出30个方程组出来。然后解方程组就行了
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int X = 35; const int n = 30; int a[X][X]; int dirx[4] = {-1,0,0,1}; int diry[4] = {0,-1,1,0}; void build(){ rep(i,5){ rep(j,6){ int x = i*6+j; a[x][x] = 1; rep(k,4){ int dx = dirx[k]+i; int dy = diry[k]+j; if(dx>=0&&dx<5 && dy>=0&&dy<6){ int y = dx*6+dy; a[x][y] = 1; } } } } } void debug(){ rep(i,30){ rep(j,31) cout<<a[i][j]<<" "; cout<<endl; } } void gauss(){ int i = 0,j = 0; while(i<n&&j<n){ int r = i; for(int k=i;k<n;k++) if(a[k][j]){ r = k; break; } if(a[r][j]){ if(r!=i) rep(k,n+1) swap(a[r][k],a[i][k]); for(int u=i+1;u<n;u++) if(a[u][j]) for(int k=j;k<n+1;k++) a[u][k] ^= a[i][k]; i ++; } j ++; } for(int i=n-2;i>=0;i--) for(int j=n-1;j>i;j--) a[i][n] ^= (a[i][j]&&a[j][n]); } int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int ncase; cin>>ncase; rep(cnt,ncase){ printf("PUZZLE #%d\n",cnt+1); memset(a,0,sizeof(a)); rep(i,n) scanf("%d",&a[i][n]); build(); gauss(); rep(i,5){ printf("%d",a[i*6][30]); for(int j=1;j<6;j++) printf(" %d",a[i*6+j][30]); puts(""); } } return 0; }
相似的几题
POJ 1681 Painter's Problem
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int X = 230; int n,m; int dirx[] = {0,0,-1,1}; int diry[] = {-1,1,0,0}; int a[X][X]; void debug(){ rep(i,n){ rep(j,n+1) cout<<a[i][j]<<" "; cout<<endl; } } void build(){ rep(i,m){ rep(j,m){ int x = i*m+j; a[x][x] = 1; rep(k,4){ int dx = dirx[k]+i; int dy = diry[k]+j; if(dx>=0&&dx<m && dy>=0&&dy<m){ int y = dx*m+dy; a[x][y] = 1; } } } } } int gauss(){ int i = 0,j = 0; while(i<n&&j<n){ int r = i; for(int k=i;k<n;k++) if(a[k][j]){ r = k; break; } if(a[r][j]){ if(r!=i) rep(k,n+1) swap(a[r][k],a[i][k]); for(int u=i+1;u<n;u++) if(a[u][j]) for(int k=j;k<n+1;k++) a[u][k] ^= a[i][k]; i ++; } j ++; } //cout<<"dsaaaaaa "<<i<<endl; for(;i<n;i++) if(a[i][n]) return -1; for(int i=n-2;i>=0;i--) for(int j=n-1;j>i;j--) a[i][n] ^= (a[i][j]&&a[j][n]); int cnt = 0; for(int i=0;i<n;i++) cnt += a[i][n]; return cnt; } int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int ncase; cin>>ncase; while(ncase--){ memset(a,0,sizeof(a)); cin>>m; char s[16]; n = m*m; rep(i,m){ scanf("%s",s); rep(j,m) a[i*m+j][n] = (s[j]=='w'); } build(); int ans = gauss(); if(ans==-1) puts("inf"); else printf("%d\n",ans); } return 0; }
POJ 1830 开关问题
/* 题目: 同关灯问题,高斯消元 */ #include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int X = 32; int n; int dirx[] = {0,0,-1,1}; int diry[] = {-1,1,0,0}; int a[X][X]; void debug(){ rep(i,n){ rep(j,n+1) cout<<a[i][j]<<" "; cout<<endl; } } int gauss(){ int i = 0,j = 0; while(i<n&&j<n){ int r = i; for(int k=i;k<n;k++) if(a[k][j]){ r = k; break; } if(a[r][j]){ if(r!=i) rep(k,n+1) swap(a[r][k],a[i][k]); for(int u=i+1;u<n;u++) if(a[u][j]) for(int k=j;k<n+1;k++) a[u][k] ^= a[i][k]; i ++; } j ++; } int cnt = n-i; for(;i<n;i++) if(a[i][n]) return -1; return 1<<cnt; } int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int ncase; cin>>ncase; while(ncase--){ cin>>n; memset(a,0,sizeof(a)); int x[X],y; rep(i,n) scanf("%d",&x[i]); rep(i,n){ scanf("%d",&y); a[i][n] = x[i] ^ y; a[i][i] = 1; } int p,q; while(scanf("%d%d",&p,&q),p||q) a[--q][--p] = 1; int ans = gauss(); if(ans==-1) puts("Oh,it's impossible~!!"); else printf("%d\n",ans); } return 0; }
URAL 1042 Central Heating
/* 题目: 同关灯问题,高斯消元 */ #include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int X = 300; int n; int a[X][X]; void debug(){ rep(i,n){ rep(j,n+1) cout<<a[i][j]<<" "; cout<<endl; } } int gauss(){ int i = 0,j = 0; while(i<n&&j<n){ int r = i; for(int k=i;k<n;k++) if(a[k][j]){ r = k; break; } if(a[r][j]){ if(r!=i) rep(k,n+1) swap(a[r][k],a[i][k]); for(int u=i+1;u<n;u++) if(a[u][j]) for(int k=j;k<n+1;k++) a[u][k] ^= a[i][k]; i ++; } j ++; } //cout<<"dsaaaaaa "<<i<<endl; for(;i<n;i++) if(a[i][n]) return -1; for(int i=n-2;i>=0;i--) for(int j=n-1;j>i;j--) a[i][n] ^= (a[i][j]&&a[j][n]); int cnt = 0; for(int i=0;i<n;i++) cnt += a[i][n]; return cnt; } int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif while(cin>>n){ memset(a,0,sizeof(a)); rep(i,n){ int y; a[i][n] = 1; while(scanf("%d",&y),y!=-1) a[--y][i] = 1; } int ans = gauss(); if(ans==-1) puts("No solution"); else{ bool ok = false; rep(i,n) if(a[i][n]){ ok?putchar(' '):ok = true; printf("%d",i+1); } puts(""); } } return 0; }
2.BZOJ 1013 [JSOI2008]球形空间产生器sphere
题目:中文题。。
分析:裸的高斯消元题
二维平面上的圆上的点与圆心的距离有(x-a)^2+(y-b)^2 = r^2
三维空间上的球上的点与球心的距离有(x-a)^2+(y-b)^2+(z-c)^2 = r^2
同理:在n维空间上的球上的点与球心的距离有sigma((xi-ai)^2) = r^2,圆心为(a1,a2,...,an)
另外,在二维平面上,可由三点(不共线)确定一个园,在三维上四点(不共线)确定一个球,同理,在n维平面上,可由n+1个点(不共线)确定一个n维的球。
这样,题目就可以转化为n+1个方程组,但是是平方级别的,如何转化为一维的?
我们不妨对于相邻的两个方程组左右分别相减,可以发现:
2*(x21 - x11)*x1 + 2*(x22 - x12)*x2 +...+2*(x2n - x1n) = (x21^2 - x11^1)+...+(x2n^2 - x1n^2)
这样,由n+1个方程组就可以转化为了n个一维的方程组了。下面,直接用高斯消元法即 可解决该问题
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; const int X = 20; #define esp 1e-8 double dp[X][X]; double a[X][X],b[X][X]; double f[X]; int n; double gauss(){ for(int i=1;i<=n;i++){ int x = i; for(int j=i+1;j<=n;j++) if(fabs(a[j][i]-a[x][i])>esp) x = j; if(x!=i) for(int j=1;j<=n+1;j++) swap(a[i][j],a[x][j]); for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>esp){ double temp = a[j][i] / a[i][i]; for(int k=i;k<=n+1;k++) a[j][k] -= temp*a[i][k]; } } for(int i=n;i;i--){ double temp = a[i][n+1]; for(int j=i+1;j<=n;j++) temp -= f[j]*a[i][j]; f[i] = temp / a[i][i]; } } int main(){ cin>>n; for(int i=1;i<n+2;i++) for(int j=1;j<=n;j++) scanf("%lf",&b[i][j]); for(int i=1;i<=n;i++){ double temp = 0; for(int j=1;j<=n;j++){ temp += b[i+1][j]*b[i+1][j]-b[i][j]*b[i][j]; a[i][j] = 2*(b[i+1][j]-b[i][j]); } a[i][n+1] = temp; } gauss(); for(int i=1;i<n;i++) printf("%.3lf ",f[i]); printf("%.3lf\n",f[n]); return 0; }
二.矩阵快速幂
1.poj 3070 Fibonacci
题目:求斐波那契数列第n项的后四位数
分析:由于n很大,我们可以构造矩阵A,B
A = 1 1 B = f1
1 0 f0
则 A^(n-1) * B = fn
fn-1
所以我们可以直接利用矩阵快速幂来求fn%10000
/* 题目: 斐波那契数列的矩阵算法 分析: 裸的矩阵快速幂 */ #include <iostream> #include <cstring> #include <cstdio> using namespace std; const int X = 5; const int mod = 10000; int n; class matrix{ public: int a[X][X]; int size; int mod; matrix(){ memset(a,0,sizeof(a)); } matrix(int _size,int _mod):size(_size),mod(_mod){ memset(a,0,sizeof(a)); } void setE(){ for(int i=0;i<size;i++) a[i][i] = 1; } matrix operator * (matrix p){ matrix c(size,mod); for(int i=0;i<size;i++) for(int j=0;j<size;j++) for(int k=0;k<size;k++){ c.a[i][j] += a[i][k]*p.a[k][j]; c.a[i][j] %= mod; } return c; } matrix pow(int exp){ matrix cur = *this; matrix c(size,mod); c.setE(); while(exp){ if(exp&1) c = c*cur; cur = cur*cur; exp = exp>>1; } return c; } void display(){ for(int i=0;i<size;i++){ for(int j=0;j<size;j++) cout<<a[i][j]<<" "; cout<<endl; } } }; int main(){ freopen("sum.in","r",stdin); while(scanf("%d",&n),n!=-1){ if(!n){ puts("0"); continue; } matrix ans(2,mod); ans.a[0][0] = ans.a[0][1] = ans.a[1][0] = 1; ans.a[1][1] = 0; ans = ans.pow(n-1); printf("%d\n",ans.a[0][0]); } return 0; }
相似的题目:
hdu 1005 number sequence
/* 题目: f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7. 给出A,B求f(n)模7 分析: 我们可以构造一个矩阵 [ a b ] * [ fn-1 ] = [ fn ] [ 1 0 ] [ fn-2 ] [ fn-1 ] 最后发现最要求左边的矩阵的(n-2)次幂后所得的上面两项的和值就是 fn,所以用到了矩阵的快速幂可以做~~ */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int X = 3; class matrix{ public: int a[X][X]; matrix(){ memset(a,0,sizeof(a)); } matrix(int _size,int _mod):size(_size),mod(_mod){ memset(a,0,sizeof(a)); } matrix operator * (matrix p){ matrix c(size,mod); for(int i=0;i<size;i++) for(int j=0;j<size;j++) for(int k=0;k<size;k++){ c.a[i][j] += a[i][k]*p.a[k][j]; c.a[i][j] %= mod; } return c; } void setE(){ for(int i=0;i<size;i++) a[i][i] = 1; } matrix pow(int exp){ matrix temp(size,mod); temp.setE(); matrix cur = *this; while(exp){ if(exp&1) temp = temp*cur; cur = cur*cur; exp = exp>>1; } return temp; } private: int size; int mod; }; int main(){ freopen("sum.in","r",stdin); int a,b,n; while(cin>>a>>b>>n,a||b||n){ if(n==1){ cout<<1<<endl; continue; } else if(n==2){ cout<<1<<endl; continue; } matrix ans(2,7); ans.a[0][0] = a; ans.a[0][1] = b; ans.a[1][0] = 1; ans.a[1][1] = 0; ans = ans.pow(n-2); cout<<(ans.a[0][0]+ans.a[0][1])%7<<endl; } return 0; }
2.HOJ 1991 Happy 2005
题目:
给出n,问2005^n的各个因子数之和对29取模
分析:
2005 = 5*401,我们可以对于401进行分类:
401^0 : 1 5 5^2 ... 5^n
401^1 : 401 401*5 401*5^2 ... 401*5^n
.
.
.
401^n : 401^n 401^n*5 401^n*5^2 ... 401^n*5^n
由此我们可以发现,问题可以转换为
(1+401+401^2+...401^n)*(1+5+5^2+...+5^n)%29
方法一:
二分再二分。首先,a^n用一次二分,求和的时候再用一次二分。
a^n二分的时候就是快速幂。
求和二分:
A+A^2+A...+A^(2k+1)= A+A^2+...+A^k+A^(k+1)+A^(k+1)*(A+A^2+...+A^k).
A+A^2+...+A^2k = A+A^2+...+A^k+A^k*(A+A^2+...+A^k).
方法二:
构造矩阵matrix如下:
A 1
0 1
我们发现matrix^(n+1)项的时候,第一行第二列就是问题所求
所以在求A+A^2+A^3+...+A^k % 29的时候,我们可以直接转化为对矩阵进行
快速幂取模。
我下面的代码为构造矩阵求解。。。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; #define debug puts("here"); const int X = 3; const int MOD = 29; struct Matrix{ ll a[X][X]; int n; int mod; Matrix(){} Matrix(int _n,int _mod):n(_n),mod(_mod){ memset(a,0,sizeof(a)); } void setE(){ memset(a,0,sizeof(a)); for(int i=1;i<=n;i++) a[i][i] = 1; } Matrix operator * (Matrix p){ Matrix c(n,mod); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++){ c.a[i][j] += a[i][k]*p.a[k][j]; c.a[i][j] %= mod; } return c; } Matrix pw(int exp){ Matrix cur = *this; Matrix c(n,MOD); c.setE(); while(exp>0){ if(exp&1) c = c*cur; cur = cur*cur; exp = exp>>1; } return c; } void di(){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) cout<<a[i][j]<<" "; cout<<endl; } } void init(int x){ a[1][2] = a[2][2] = 1; a[2][1] = 0; a[1][1] = x; } }matrix; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif ll n; while(cin>>n,n){ Matrix a = Matrix(2,MOD); a.init(5); a = a.pw(n+1); ll ans = a.a[1][2]%MOD; a.init(401); a = a.pw(n+1); ans = ans*a.a[1][2]%MOD; cout<<ans<<endl; } return 0; }
相似的题目:
hoj 2635 Weights
/* 题目: 直接求1+3^1+...+3^n的和 分析: 构造矩阵matrix如下: A 1 0 1 我们发现matrix^(n+1)项的时候,第一行第二列就是问题所求 所以在求A+A^2+A^3+...+A^k % p的时候,我们可以直接转化为对矩阵进行 快速幂取模。 我下面的代码为构造矩阵求解。。。 */ #include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; #define debug puts("here"); const int X = 3; const int MOD = 9999997; struct Matrix{ ll a[X][X]; int n; int mod; Matrix(){} Matrix(int _n,int _mod):n(_n),mod(_mod){ memset(a,0,sizeof(a)); } void setE(){ memset(a,0,sizeof(a)); for(int i=1;i<=n;i++) a[i][i] = 1; } Matrix operator * (Matrix p){ Matrix c(n,mod); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++){ c.a[i][j] += a[i][k]*p.a[k][j]; c.a[i][j] %= mod; } return c; } Matrix pw(int exp){ Matrix cur = *this; Matrix c(n,MOD); c.setE(); while(exp>0){ if(exp&1) c = c*cur; cur = cur*cur; exp = exp>>1; } return c; } void di(){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) cout<<a[i][j]<<" "; cout<<endl; } } void init(int x){ a[1][2] = a[2][2] = 1; a[2][1] = 0; a[1][1] = x; } }matrix; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif ll n; while(cin>>n){ Matrix a = Matrix(2,MOD); a.init(3); a = a.pw(n); //a.di(); ll ans = a.a[1][2]%MOD; cout<<ans<<endl; } return 0; }
poj 3233 Matrix Power Series
/* 题目: 求出 S = A + A^2 + A^3 + … + A^k. 分析: 解法一 Let B= A I 0 I B^(k+1) = A^k I+A+...+A^k 0 I 解法二 设f[n]=A^1+A^2+....A^n; 当n是偶数,f[n]=f[n/2]+f[n/2]*A^(n/2); 但n是奇数,f[n]=f[n-1]+A^(n); */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int X = 31<<2; int n,m,k; class matrix{ public: int size; int mod; int a[X][X]; matrix(){ memset(a,0,sizeof(a)); } matrix(int _size,int _mod):size(_size),mod(_mod){ memset(a,0,sizeof(a)); } void setE(){ for(int i=0;i<2*size;i++) a[i][i] = 1; } void print(){ for(int i=0;i<size;i++){ printf("%d",a[i][size]); for(int j=1;j<size;j++) printf(" %d",a[i][j+size]); puts(""); } } matrix operator * (matrix p){ matrix c(size,mod); for(int i=0;i<2*size;i++) for(int j=0;j<2*size;j++) for(int k=0;k<2*size;k++){ c.a[i][j] += a[i][k]*p.a[k][j]; c.a[i][j] %= mod; } return c; } void operator -- (){ for(int i=0;i<size;i++) a[i][i+size] = (--a[i][i+size]+mod)%mod; } matrix pow(int exp){ matrix temp(size,mod); temp.setE(); matrix cur = *this; while(exp){ if(exp&1) temp = temp*cur; cur = cur*cur; exp = exp>>1; } return temp; } void display(){ for(int i=0;i<2*size;i++){ for(int j=0;j<2*size;j++) cout<<a[i][j]<<" "; cout<<endl; } } }; int main(){ freopen("sum.in","r",stdin); while(cin>>n>>k>>m){ matrix ans(n,m); for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%d",&ans.a[i][j]); for(int i=n;i<2*n;i++) ans.a[i-n][i] = ans.a[i][i] = 1; ans = ans.pow(k+1); --ans; ans.print(); } return 0; }
3.线性递推
对于线性递推f[k] = a*f[k-1]+b*f[k-2]+c*f[k-3]...z*f[0]
我们可以构造矩阵A,B:
A = 0 1 0... B = f[k-1]
0 0 1... f[k-2]
.......... ........
a b c... f[0]
我们可以发现
A*B = f[k]
f[k-1]
........
f[1]
所以A^(n-k) * B = f[n]
f[n-1]
....
f[n-k+1]
HOJ 1790 Firepersons
题目:
线性递推关系,an=Σ1<=i<=k*an-i*bi,问ai
分析:
可以构造矩阵A如下
0 1 0 ...0
0 0 1 ...0
...
0 0 0 ...1
bk bk-1 bk-2...b0
矩阵B为
a0
a1
a2
...
ak-1
则ai = ai(i<k)
= A^(i-k+1)*B最后一行的元素
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define debug puts("here") const int MOD = 10000; const int X = 102; class Matrix{ public: int n,m; int a[X][X]; Matrix(){ memset(a,0,sizeof(a)); } Matrix(int _n,int _m):n(_n),m(_m){ memset(a,0,sizeof(a)); } Matrix operator * (Matrix p){ int q = p.m; Matrix c(n,q); for(int i=0;i<n;i++) for(int j=0;j<q;j++) for(int k=0;k<m;k++) c.a[i][j] = (c.a[i][j]+a[i][k]*p.a[k][j])%MOD; return c; } void getE(){ memset(a,0,sizeof(a)); for(int i=0;i<n;i++) a[i][i] = 1; } Matrix bin(int exp){ Matrix temp(n,n); temp.getE(); Matrix cur = *this; while(exp>0){ if(exp&1) temp = temp*cur; cur = cur*cur; exp = exp >> 1; } return temp; } void di(){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++) cout<<a[i][j]<<" "; cout<<endl; } cout<<endl; } }; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int n; while(cin>>n,n){ Matrix a = Matrix(n,n); Matrix b = Matrix(n,1); for(int i=0;i<n-1;i++) a.a[i][i+1] = 1; for(int i=0;i<n;i++) scanf("%d",&b.a[i][0]); for(int i=0;i<n;i++) scanf("%d",&a.a[n-1][n-i-1]); int exp; cin>>exp; if(exp<n){ printf("%d\n",b.a[exp][0]); continue; } exp ++; exp -= n; a = a.bin(exp); a = a*b; printf("%d\n",a.a[n-1][0]); } return 0; }
BZOJ 2875: [ NOI2012 ] 随机数生成器
http://www.lydsy.com/JudgeOnline/problem.php?id=2875
题目:f[n+1] = (f[n]+c)%m , 给出f[0],n,m,c,g,求f[n]%g
分析:很明显可以构造矩阵
A = a 1 B = f[0]
0 1 c
但是由于数据太大了,所以在矩阵乘法中间计算时会溢出,所以我们需要在乘的时候做一下处理。改为跟快速幂乘法相似的计算方式来防止溢出。具体看代码
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef unsigned long long ll; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int X = 5; ll MOD; class Matrix{ public: int n,m; ll a[X][X]; ll cal(ll x,ll y){ ll sum = 0; while(y>0){ if(y&1) sum = (sum+x)%MOD; x = (x<<1)%MOD; y >>= 1; } return sum; } Matrix(){ memset(a,0,sizeof(a)); } Matrix(int _n,int _m):n(_n),m(_m){ memset(a,0,sizeof(a)); } Matrix operator * (Matrix p){ int q = p.m; Matrix c(n,q); for(int i=0;i<n;i++) for(int j=0;j<q;j++) for(int k=0;k<m;k++) c.a[i][j] = (c.a[i][j]+cal(a[i][k],p.a[k][j]))%MOD; return c; } void getE(){ memset(a,0,sizeof(a)); for(int i=0;i<n;i++) a[i][i] = 1; } Matrix bin(ll exp){ Matrix temp(n,n); temp.getE(); Matrix cur = *this; while(exp>0){ if(exp&1) temp = temp*cur; cur = cur*cur; exp = exp >> 1; } return temp; } void di(){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++) cout<<a[i][j]<<" "; cout<<endl; } cout<<endl; } }; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif ll m,a,c,x0,n,g; while(cin>>m>>a>>c>>x0>>n>>g){ MOD = m; Matrix ans(2,2); ans.a[0][0] = a; ans.a[0][1] = ans.a[1][1] = 1; ans = ans.bin(n); //ans.di(); Matrix temp(2,1); temp.a[0][0] = x0; temp.a[1][0] = c; ans = ans*temp; cout<<ans.a[0][0]%g<<endl; } return 0; }
HOJ 2060 Fibonacci Problem Again
题目:
计算斐波那契数列[a,b]的和值对于1e9取模
分析:
对于斐波那契求第n项,我们可以构造矩阵
A = 1 1 B = f[n]
1 0 f[n-1]
则f[n]为矩阵 A^n * B的第一项
对于这题,我们可以额外构造多一维的矩阵出来为
1 1 0 f[n]
A = 1 0 0 B = f[n-1]
1 0 1 sum[n-1]
我们同样可以算出 sum[n] = A^n * B 的第三项
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int X = 5; const ll MOD = 1e9; class Matrix{ public: int n,m; ll a[X][X]; Matrix(){ memset(a,0,sizeof(a)); } Matrix(int _n,int _m):n(_n),m(_m){ memset(a,0,sizeof(a)); } Matrix operator * (Matrix p){ int q = p.m; Matrix c(n,q); for(int i=0;i<n;i++) for(int j=0;j<q;j++) for(int k=0;k<m;k++) c.a[i][j] = (c.a[i][j]+a[i][k]*p.a[k][j])%MOD; return c; } void getE(){ memset(a,0,sizeof(a)); for(int i=0;i<n;i++) a[i][i] = 1; } Matrix bin(int exp){ Matrix temp(n,n); temp.getE(); Matrix cur = *this; while(exp>0){ if(exp&1) temp = temp*cur; cur = cur*cur; exp = exp >> 1; } return temp; } void di(){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++) cout<<a[i][j]<<" "; cout<<endl; } cout<<endl; } }; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int n,m; while(scanf("%d%d",&n,&m),n||m){ Matrix a = Matrix(3,3); a.a[0][0] = a.a[0][1] = a.a[1][0] = a.a[2][0] = a.a[2][2] = 1; Matrix f = Matrix(3,1); f.a[0][0] = 1; f.a[1][0] = 1; f.a[2][0] = 1; ll pre = 0; ll now = 0; if(n){ Matrix x = a.bin(n-1); x = x*f; pre = x.a[2][0]; } Matrix y = a.bin(m); y = y*f; now = y.a[2][0]; ll mod = ll(1000000000); cout<<(now-pre+mod)%mod<<endl; } return 0; }
(HOJ 2255 Not Fibonacci这题跟上面的题目基本一模一样,代码略)
HOJ 2930 Perfect Fill IIl
详情看上一篇博文 http://www.cnblogs.com/yejinru/archive/2013/02/01/2888368.html
4.HDU 2157 How many ways
题目:
给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值
分析:
把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可。
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int MOD = 1000; const int X = 25; class Matrix{ public: int n,m; int a[X][X]; Matrix(){ memset(a,0,sizeof(a)); } Matrix(int _n,int _m):n(_n),m(_m){ memset(a,0,sizeof(a)); } Matrix operator * (Matrix p){ int q = p.m; Matrix c(n,q); for(int i=0;i<n;i++) for(int j=0;j<q;j++) for(int k=0;k<m;k++) c.a[i][j] = (c.a[i][j]+a[i][k]*p.a[k][j])%MOD; return c; } void getE(){ memset(a,0,sizeof(a)); for(int i=0;i<n;i++) a[i][i] = 1; } Matrix bin(int exp){ Matrix temp(n,n); temp.getE(); Matrix cur = *this; while(exp>0){ if(exp&1) temp = temp*cur; cur = cur*cur; exp = exp >> 1; } return temp; } void di(){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++) cout<<a[i][j]<<" "; cout<<endl; } cout<<endl; } }; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int n,m,x,y,k; while(scanf("%d%d",&n,&m),n||m){ Matrix a = Matrix(n,n); while(m--){ scanf("%d%d",&x,&y); a.a[x][y] = 1; } int cnt; cin>>cnt; while(cnt--){ scanf("%d%d%d",&x,&y,&k); Matrix temp = a.bin(k); printf("%d\n",temp.a[x][y]); } } return 0; }
不会分类的几题:
vijos 1049 送给圣诞夜的礼品
顺次给出m个置换,反复使用这m个置换对初始序列进行操作,问k次置换后的序列。m<=10, k<2^31。
首先将这m个置换“合并”起来(算出这m个置换的乘积),然后接下来我们需要执行这个置换k/m次(取整,若有余数则剩下几步模拟即可)。
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define debug puts("here") const int MAXN = 102; const int MAXM = 11; int n,m; class Matrix{ public: int a[MAXN][MAXN]; Matrix(){ memset(a,0,sizeof(a)); } void setE(){ memset(a,0,sizeof(a)); for(int i=0;i<n;i++) a[i][i] = 1; } Matrix operator * (Matrix b){ Matrix c = Matrix(); for(int i=0;i<n;i++) for(int j=0;j<n;j++) for(int k=0;k<n;k++) c.a[i][j] += a[i][k]*b.a[k][j]; return c; } Matrix bin(int exp){ Matrix ans; Matrix cur = *this; ans.setE(); while(exp>0){ if(exp&1) ans = ans*cur; cur = cur*cur; exp >>= 1; } return ans; } void di(){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++) cout<<a[i][j]<<" "; cout<<endl; } cout<<endl; } }; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int k; while(cin>>n>>m>>k){ int x; Matrix ans; ans.setE(); Matrix ret[12]; ret[0].setE(); for(int i=0;i<m;i++){ Matrix cur; for(int j=0;j<n;j++){ scanf("%d",&x); cur.a[j][x-1] = 1; } ret[i+1] = cur*ret[i]; //ret[i+1].di(); ans = cur*ans; } ans = ans.bin(k/m); ans = ret[k%m]*ans; //ans.di(); for(int i=0;i<n;i++) for(int j=0;j<n;j++){ if(ans.a[i][j]){ if(i) cout<<" "; cout<<j+1; break; } } cout<<endl; } return 0; }
HOJ 2446 Cellular Automaton
题目:
在一个环中有n个格子,每个格子的值为ai,距离该格子不足d的所有格子的和对于
m取余为新的值,问第k次变换后的所有n个格子的值
分析:
很容易可以构造出一个循环的矩阵出来,但是如果是O(n^3*logn)会TLE。
我们可以注意到循环矩阵a * b只需要计算a的第一行*b,然后下面的移位均可以得
到。时间为O(n^2*logn)
#include <set> #include <map> #include <cmath> #include <queue> #include <stack> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; #define debug puts("here") #define rep(i,n) for(int i=0;i<n;i++) #define foreach(i,vec) for(unsigned i=0;i<vec.size();i++) #define pb push_back const int X = 505; ll a[X][X],c[X][X]; ll ans[X]; int main(){ #ifndef ONLINE_JUDGE freopen("sum.in","r",stdin); #endif int n,m,k,d; while(~scanf("%d%d%d%d",&n,&m,&d,&k)){ rep(i,n) scanf("%lld",&ans[i]); rep(i,n) rep(j,n) if( (i-j+n)%n<=d || (j-i+n)%n<=d ) a[i][j] = 1; else a[i][j] = 0; while(k>0){ if(k&1){ rep(i,n){ c[0][i] = 0; rep(j,n) c[0][i] += ans[j]*a[j][i]; } rep(i,n) ans[i] = c[0][i]%m; } rep(i,n){ c[0][i] = 0; rep(j,n) c[0][i] += a[0][j]*a[j][i]; } rep(i,n) rep(j,n) if(i==0) a[i][j] = c[i][j]%m; else a[i][j] = a[i-1][(j-1+n)%n]; k >>= 1; } rep(i,n-1) printf("%lld ",ans[i]); printf("%lld\n",ans[n-1]); } return 0; }
压缩矩阵
poj 3318 Matrix Multiplication
题目:
判断矩阵a * b == c
分析:
方法一:
O(n^3)算法提示会TLE,但是原矩阵是一个稀疏矩阵,所以可
以在相乘的时候判断是否为0,这样同样不会TLE~~
方法二:
压缩矩阵,左乘1*n的矩阵,使得左边以及右边都变成1*n的
矩阵,然后两边判断是否相等就行了~~但是如果这样压缩的话,
不保证每个元素的特性,所以这个1*n的矩阵得要体现特性,构
造的时候可以取随机数,或者令(1,2...n)
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int X = 505; #define debug puts("here"); typedef __int64 ll; int n; void mul(ll a[X][X],ll *b){ ll temp[X] = {0}; for(int i=0;i<n;i++) for(int j=0;j<n;j++) temp[i] += b[j]*a[j][i]; for(int i=0;i<n;i++) b[i] = temp[i]; } bool eq(ll a[],ll b[]){ for(int i=0;i<n;i++) if(a[i]!=b[i]) return false; return true; } ll a[X][X],b[X][X],c[X][X]; ll q[X],p[X]; int main(){ freopen("sum.in","r",stdin); while(cin>>n){ for(int i=0;i<n;i++) q[i] = p[i] = i+1; for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%I64d",&a[i][j]); for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%I64d",&b[i][j]); for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%I64d",&c[i][j]); mul(a,p); mul(b,p); mul(c,q); if(eq(p,q)) puts("YES"); else puts("NO"); } return 0; }
以后若还有的话,会更新一下的。。。