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;
}
本文介绍如何利用矩阵快速幂解决一类涉及大量重复操作序列的问题。通过构造特定的矩阵来模拟不同类型的命令操作,再借助快速幂进行高效计算,最终解决大规模重复操作导致的计算复杂度问题。
240

被折叠的 条评论
为什么被折叠?



