矩阵总结 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;
}
以后若还有的话,会更新一下的。。。

浙公网安备 33010602011771号