POJ-3735-Training little cats-构造矩阵+矩阵快速幂+稀疏矩阵乘法优化

http://poj.org/problem?id=3735

题意:

n只猫,三种命令:

1、第i只猫吃掉所有花生;

2、第i只猫得到一个花生;

3、交换第i,j只猫的花生;

先由k个 这些命令组成一个操作序列

然后重复操作序列m次,

n,k<=100,m<=1e9

m的次数那么大,可以用构造矩阵,然后用快速幂的方法

引用大神http://blog.csdn.net/magicnumber/article/details/6217602的图片

初始我们开一个1行n+1列的矩阵存放每只猫的初始值  第n+1行的前n个元素表示每只猫的花生数

org=【

1 0 0 0

0 1 0 0

0 0 1 0

x y z 1

在n+1阶单位矩阵的基础上,

给第1 只猫加1个花生的操作矩阵G为:

1 0 0 0

0 1 0 0

0 0 1 0

1 0 0 1   第n+1行的第i列加1

 

让第2只猫吃完所有花生的操作矩阵E为:

1 0 0 0

0 0 0 0

0 0 1 0

0 0 0 1  第i列清空为0


 

交换第1 第2 只猫的操作矩阵S为:

0 1 0 0

1 0 0 0 交换第i第j列元素

0 0 1 0

0 0 0 1


********************************************************

我们可以发现,让org初始矩阵左乘一个G矩阵,得到

1 0 0 0

0 1 0 0

0 0 1 0

(x+1)  Y Z 1

恰好就是X加了1

我们可以发现,让org初始矩阵左乘一个E矩阵,得到

1 0 0 0

0 1 0 0

0 0 1 0

X 0 Z 1

恰好就是吃光了Y

我们可以发现,让org初始矩阵左乘一个S矩阵,得到

1 0 0 0

0 1 0 0

0 0 1 0

y x Z 1

恰好就是交换了Y和X的花生

******************************************************

那么对于k^m次操作,我们先处理好前k次的操作

也就是用org初始矩阵不断乘G/E/S矩阵  得到前k次的结果  (PS:由于我们可以直接知道k次每次乘了操作矩阵后的结果(交换两列、清空行,指定位置加1),就可以不必模拟k次,直接得到k次的结果了 )


接下来就是对k次得到结果矩阵X  连续左乘m次。。。这里就用个快速幂就OK,然而每次乘法O(N^3),log(m)≈26  所以还需要优化一下乘法部分,

因为显然大部分元素为0,是稀疏矩阵,我们可以用系数矩阵乘法优化

  一般矩阵乘法:

Matrix mul(Matrix a, Matrix b) //矩阵相乘
{
    Matrix res;
    for(int i = 0; i < k; i++)
        for(int j = 0; j < k; j++)
        {
            res.mat[i][j] = 0;
            for(int t = 0; t < k; t++)
            {
                res.mat[i][j] += a.mat[i][t] * b.mat[t][j];
                res.mat[i][j] %= mod;
            }
        }

    return res;
}

稀疏矩阵:

matrix mul(matrix a,matrix b)
{
	matrix res;
	 memset(res.m,0,sizeof(res.m));
	__int64 i,j,k;
	for (i=1;i<=n+1;i++)
	{
		for (j=1;j<=n+1;j++)		
		{
		 
            if (a.m[i][j])		//稀疏矩阵乘法(非O(n^3)), 即知道答案为零直接跳过
			for (k=1;k<=n+1;k++)
			{
				res.m[i][k]+=a.m[i][j]*b.m[j][k];
			}
		}
	}
	return res;

}


从而解决问题,

难点是稀疏矩阵优化/构造操作矩阵


代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
struct matrix
{
	__int64 m[105][105];
};

matrix unit_org;
matrix unit; 

__int64 n,k;
int m;
void init()
{
	char op;
	__int64 num,num1,num2,i,j;
	for (j=1;j<=k;j++)
	{
		scanf("%c",&op);
		if (op=='g')
		{
			scanf("%I64d",&num);
			getchar();
			unit.m[n+1][num]++;   //第num个+1
		}
		if (op=='e')
		{
			scanf("%I64d",&num);
			getchar();
			for (i=1;i<=n+1;i++)
				unit.m[i][num]=0; //第num列置为零
		}
		if (op=='s')
		{
			scanf("%I64d%I64d",&num1,&num2);
			getchar();
			if (num1==num2) continue;
			for (i=1;i<=n+1;i++)			//交换两列
			{
				num=unit.m[i][num1];
			    unit.m[i][num1]=unit.m[i][num2];
				unit.m[i][num2]=num;
			}
		}
	}
}
matrix mul(matrix a,matrix b)
{
	matrix res;
	 memset(res.m,0,sizeof(res.m));
	__int64 i,j,k;
	for (i=1;i<=n+1;i++)
	{
		for (j=1;j<=n+1;j++)		
		{
		 
            if (a.m[i][j])		//稀疏矩阵乘法(非O(n^3)), 即知道答案为零直接跳过
			for (k=1;k<=n+1;k++)
			{
				res.m[i][k]+=a.m[i][j]*b.m[j][k];
			}
		}
	}
	return res;

}
matrix pow_mi(matrix a,int b)
{
	matrix res=unit_org;
	while(b)
	{

		if (b&1)
			res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
int main()
{
	__int64 i,j;

	while(scanf("%I64d%d%I64d",&n,&m,&k)!=EOF)
	{
		getchar();
		if (!n&&!m&&!k) break;
		memset(unit.m,0,sizeof(unit.m));
		for (i=1;i<=101;i++)
			unit.m[i][i]=1;
		unit_org=unit;
		init();
		matrix ans=	pow_mi(unit,m);
		for (i=1;i<=n;i++)
		{
			if (i!=1) printf(" ");
			printf("%I64d",ans.m[n+1][i]);
		}
		printf("\n");



	}
		return 0;

	}







你可能感兴趣的:(POJ-3735-Training little cats-构造矩阵+矩阵快速幂+稀疏矩阵乘法优化)