矩阵总结 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;
}

 

以后若还有的话,会更新一下的。。。

原文地址:https://www.cnblogs.com/yejinru/p/2890441.html