| ||||||||||
| Online Judge | Problem Set | Authors | Online Contests | User | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest | |||||||||
我来个最详细的解题报告/*
题意:有n只猫咪,开始时每只猫咪有花生0颗,现有一组操作,由下面三个中的k个操作组成:
1. g i:给i只猫咪一颗花生米;
2. e i:让第i只猫咪吃掉它拥有的所有花生米;
3. s i:j 将猫咪i与猫咪j的拥有的花生米交换。
现将上述一组操作做m次后,问每只猫咪有多少颗花生?
思路:m达到10^9,显然不能直接算。
因为k个操作给出之后就是固定的,所以想到用矩阵,矩阵快速幂可以把时间复杂度降到O(log m)。问题转化为如何构造转置矩阵?
有一个很好的办法就是添加一个辅助,使初始矩阵变为一个n+1元组,编号为0到n,下面以3个猫为例:
定义初始矩阵A = [x y z 1],最后一个元素固定为1,0~n-1分别为对应的每一只猫所拥有的花生数。
再定义一个n+1维的方阵Mat,初始化为单位矩阵,即Mat最初为:
|1 0 0 0|
|0 1 0 0|
|0 0 1 0|
|0 0 0 1|
,最后一行的前n个元素表示3只猫所拥有的花生数x、y、z,最开始的时候,3只猫都没有花生米,则Mat转化为:
|1 0 0 0|
|0 1 0 0|
|0 0 1 0|
|0 0 0 1|
,对于第一种操作g i,给i只猫咪一颗花生米,在单位矩阵基础上使Mat[n][i]变为1,例如g 1,表示给第0只猫咪一颗花生米:
|1 0 0 0|
|0 1 0 0|
|0 0 1 0|
|1 0 0 1|
,显然
|1 0 0 0|
A*Mat = [x y z 1] * |0 1 0 0| = [x+1 y z 1]
|0 0 1 0|
|1 0 0 1|
;对于第二种操作e i,让第i只猫咪吃掉它拥有的所有花生米,在单位矩阵基础上使Mat[i][i] = 0, 例如e 2,让第1只猫咪吃掉它拥有的所有花生米,需要使Mat[1][1]为0:
|1 0 0 0|
|0 0 0 0|
|0 0 1 0|
|0 0 0 1|
,显然
|1 0 0 0|
A*Mat = [x y z 1] * |0 0 0 0| = [x 0 z 1]
|0 0 1 0|
|0 0 0 1|
;对于第三种操作s i j,将猫咪i与猫咪j的拥有的花生米交换,在单位矩阵基础上使第i列与第j互换,例如s 2 3,让第1只猫咪和第2只猫咪的花生互换,需要在Mat矩阵中将第1列和第2列互换:
|1 0 0 0|
|0 0 1 0|
|0 1 0 0|
|0 0 0 1|
,显然
|1 0 0 0|
A*Mat = [x y z 1] * |0 0 1 0| = [x z y 1]
|0 1 0 0|
|0 0 0 1|
。现在,对于每一个操作都可以得到一个操作矩阵,把k个操作矩阵相乘可以得到一个新的矩阵T。
A * T 表示我们经过一组操作,类似我们可以得到经过m组操作的矩阵为 A * (T ^ m), 最终矩阵的[n][0~n-1]即为答案。
上述的做法比较直观,但是实现过于麻烦,因为要构造k个不同矩阵。
观察以下矩阵相乘的结果:
|1 0 0 0|
[p q r 1] * |0 1 0 0| = [p+x q+y z+r 1]
|0 0 1 0|
|x y z 1|
相乘的结果是把p平移了x,q平移了y,r平移了z。
假设x、y、z分别表示某个操作前,3只猫咪已有的花生数,将A矩阵改为[0 0 0 1] ,
再将Mat矩阵改为
|1 0 0 0|
|0 1 0 0|
|0 0 1 0|
|x y z 1|
则:
|1 0 0 0|
A*Mat = [0 0 0 1] * |0 1 0 0| = [x y z 1]
|0 0 1 0|
|x y z 1|
相当于是获取了Mat矩阵的最后一行。再观察下面的矩阵相乘:
此时,
对于第一种操作g i,给i只猫咪一颗花生米,例如g 1,表示给第0只猫咪一颗花生米,可以用下面的矩阵相乘表示:
|1 0 0 0| |1 0 0 0| | 1 0 0 0|
|0 1 0 0| * |0 1 0 0| = | 0 1 0 0|
|0 0 1 0| |0 0 1 0| | 0 0 1 0|
|x y z 1| |1 0 0 1| |x+1 y z 1|
相当于给相乘前第一个矩阵的最后一行的第i-1列的元素加1,结果矩阵的最后一行的前n个元素表示执行g 1操作后n只猫咪各自拥有的花生数。
对于第二种操作e i,让第i只猫咪吃掉它拥有的所有花生米,例如e 2,让第1只猫咪吃掉它拥有的所有花生米,可以用下面的矩阵相乘表示:
|1 0 0 0| |1 0 0 0| | 1 0 0 0|
|0 1 0 0| * |0 0 0 0| = | 0 0 0 0|
|0 0 1 0| |0 0 1 0| | 0 0 1 0|
|x y z 1| |0 0 0 1| | x 0 z 1|
相当于给相乘前第一个矩阵的第i-1列的所有元素清0,结果矩阵的最后一行的前n个元素表示执行e 2操作后n只猫咪各自拥有的花生数。
对于第三种操作s i j,将猫咪i与猫咪j的拥有的花生米交换,例如s 2 3,让第1只猫咪和第2只猫咪的花生互换,可以用下面的矩阵相乘表示:
|1 0 0 0| |1 0 0 0| | 1 0 0 0|
|0 1 0 0| * |0 0 1 0| = | 0 0 1 0|
|0 0 1 0| |0 1 0 0| | 0 1 0 0|
|x y z 1| |0 0 0 1| | x z y 1|
相当于给相乘前第一个矩阵的第i-1列和第j-1列互换,结果矩阵的最后一行的前n个元素表示执行s 2 3操作后n只猫咪各自拥有的花生数。
这样实现的话,我们始终在处理一个矩阵,免去构造k个矩阵的麻烦。
样例说明:初始状态|0 0 0 1|,
|1 0 0 0| |1 0 0 0| |1 0 0 0| |1 0 0 0| |0 1 0 0| |0 1 0 0| |0 0 0 0|
|0 1 0 0| |0 1 0 0| |0 1 0 0| |0 1 0 0| |1 0 0 0| |1 0 0 0| |1 0 0 0|
|0 0 1 0|->(g 1)|0 0 1 0|->(g 2)|0 0 1 0|->(g 2)|0 0 1 0|->(s 1 2)|0 0 1 0|->(g 3)|0 0 1 0|->(e 2)|0 0 1 0|
|0 0 0 1| |1 0 0 1| |1 1 0 1| |1 2 0 1| |2 1 0 1| |2 1 1 1| |2 0 1 1|
最后3只猫咪各自拥有的花生数是2、0、1。
*/
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>
#include <functional>
#include <cstdio>
using namespace std;
class Matrix
{
protected:
//long long ** arr;
long long arr[105][105];
int m;
int n;
public:
Matrix(int m, int n, int** a = NULL);
Matrix(const Matrix& rhs);
virtual ~Matrix();
long long * operator[](int i);
Matrix& operator = (const Matrix& rhs);
Matrix operator * (const Matrix& rhs) const;
};
Matrix::Matrix(int m, int n, int** a) : m(m), n(n)
{
//arr = new long long *[m];
//for (int i = 0; i < m; i++)
//{
// arr[i] = new long long [n];
// if (a && a[i])
// {
// for (int j = 0; j < n; j++)
// {
// arr[i][j] = a[i][j];
// }
// }
//}
}
Matrix::Matrix(const Matrix& rhs) : m(rhs.m), n(rhs.n)
{
//arr = new long long*[m];
//for (int i = 0; i < m; i++)
//{
// arr[i] = new long long[n];
//}
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arr[i][j] = rhs.arr[i][j];
}
}
}
Matrix::~Matrix()
{
//for (int i = 0; i < m; i++)
//{
// delete[] arr[i];
//}
//delete[] arr;
}
long long* Matrix::operator[](int i)
{
return arr[i];
}
Matrix& Matrix::operator = (const Matrix& rhs)
{
//for (int i = 0; i < m; i++)
//{
// delete[] arr[i];
//}
//delete[] arr;
m = rhs.m;
n = rhs.n;
//arr = new long long *[m];
//for (int i = 0; i < m; i++)
//{
// arr[i] = new long long [n];
//}
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arr[i][j] = rhs.arr[i][j];
}
}
return *this;
}
Matrix Matrix::operator * (const Matrix& rhs) const
{
Matrix result(this->m, rhs.n);
for (int i = 0; i < this->m; i++)
{
for (int j = 0; j < rhs.n; j++)
{
result.arr[i][j] = 0;
}
}
for (int i = 0; i < this->m; i++)
{
for (int j = 0; j < this->n; j++)
{
if (arr[i][j] != 0) // 必须这么写,否则超时,因为是稀疏矩阵
{
for (int k = 0; k < rhs.n; k++)
{
result.arr[i][k] += arr[i][j] * rhs.arr[j][k];
}
}
}
}
return result;
}
class SMatrix : public Matrix
{
public:
SMatrix(int n, int** a = NULL);
SMatrix Exp(int k);
void MakeE();
SMatrix operator * (const SMatrix& rhs) const;
};
SMatrix::SMatrix(int n, int** a) : Matrix(n, n, a)
{
}
void SMatrix::MakeE()
{
for (int i = 0; i < this->n; i++)
{
for (int j = 0; j < this->n; j++)
{
if (i == j)
{
arr[i][i] = 1;
}
else
{
arr[i][j] = 0;
}
}
}
}
SMatrix SMatrix::operator * (const SMatrix& rhs) const
{
SMatrix result(this->n);
for (int i = 0; i < this->n; i++)
{
for (int j = 0; j < rhs.n; j++)
{
result.arr[i][j] = 0;
}
}
for (int i = 0; i < this->n; i++)
{
for (int j = 0; j < this->n; j++)
{
if (arr[i][j]) // 必须这么写,否则超时
{
for (int k = 0; k < rhs.n; k++)
{
result.arr[i][k] += arr[i][j] * rhs.arr[j][k];
}
}
}
}
return result;
}
SMatrix SMatrix::Exp(int k)
{
if (k == 1)
{
return *this;
}
SMatrix result(this->n);
// 先构造一个单位矩阵
result.MakeE();
if (k == 0)
{
return result;
}
SMatrix p(*this);
while (k > 0)
{
if (k & 1) // 如果k为奇数
{
result = (p * result);
//cout << "result\n";
//result.Print();
}
p = p * p;
k >>= 1;
}
return result;
}
class TrainingCat
{
protected:
SMatrix* matrix;
//SMatrix matrix;
int n; // 猫咪数
int m; // 重复次数
int k; // 每一次有k个操作
void Once();
public:
void Solve();
};
void TrainingCat::Once()
{
matrix->MakeE();
//matrix.MakeE();
while (k-- > 0)
{
char oper[5];
scanf("%s", oper);
if (oper[0] == 'g')
{
int x = 0;
scanf("%d", &x);
x--;
(*matrix)[n][x]++;
//matrix[n][x]++;
}
else if (oper[0] == 's')
{
int x = 0, y = 0;
scanf("%d %d", &x, &y);
x--;
y--;
if (x != y)
{
for (int i = 0; i < n + 1; i++)
{
swap((*matrix)[i][x], (*matrix)[i][y]);
//swap(matrix[i][x], matrix[i][y]);
}
}
}
else
{
int x = 0;
scanf("%d", &x);
x--;
for (int i = 0; i < n + 1; i++)
{
(*matrix)[i][x] = 0;
//matrix[i][x] = 0;
}
}
}
}
void TrainingCat::Solve()
{
while (~scanf("%d %d %d", &n, &m, &k))
{
if (n == 0 && m == 0 && k == 0)
{
break;
}
matrix = new SMatrix(n + 1);
//SMatrix matrix(n + 1);
Once();
SMatrix ans = matrix->Exp(m);
//SMatrix ans = matrix.Exp(m);
for (int i = 0; i < n; i++)
{
printf("%I64d ", ans[n][i]);
}
printf("\n");
delete[] matrix;
}
}
int main()
{
TrainingCat obj;
obj.Solve();
return 0;
}
Followed by: Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator