题目连接:Training little cats
题目大意:有n只猫咪,开始时每只猫咪有花生0颗,现有一组操作,由下面三个中的k个操作组成:
1. g i 给i只猫咪一颗花生米
2. e i 让第i只猫咪吃掉它拥有的所有花生米
3. s i j 将猫咪i与猫咪j的拥有的花生米交换
现将上述一组操作做m次后,问每只猫咪有多少颗花生?
题目思路:我们想到m是1e9的次数,所以我们是不可以使用朴素的方法来计算的,所以这个时候我们可以想到使用矩阵来解决这样一个问题,我们需要有一个矩阵来计算快速幂,幂次之后得到的就是再重复一次操作,我们可以去构造一个单位矩阵,然后,g i,就使a[0][i]++;e i,就使第j列的数全部清空;s i j就使i列和j列交换,这样去模拟一下发现是可以做到重复操作的,比如这样的一个操作:
g 1;g 2;s 3 4,现在我们去模拟一下,初始矩阵(只操作这些操作一遍)为
⎧⎩⎨⎪⎪⎪⎪⎪⎪1000110000011010⎫⎭⎬⎪⎪⎪⎪⎪⎪
,然后做一次乘法后发现矩阵变成了:
⎧⎩⎨⎪⎪⎪⎪⎪⎪1000210010101001⎫⎭⎬⎪⎪⎪⎪⎪⎪
刚好是重复一次操作,然后输出第一行的数就好了,需要注意的是n为100,直接裸乘复杂度会炸,所以我们需要使用优化,因为矩阵中有很多的非零数,所以我们在处理的时候需要处理一下0以防止复杂度爆炸,这样处理一下就好了
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 105;
ll N,M,K,x,y;
struct mat{
ll m[maxn][maxn];
void Clear(){memset(m,0,sizeof(m));}
void unit(){
Clear();
for(int i = 0;i <= maxn-1;i++) m[i][i] = 1;
}
};
mat operator *(mat a,mat b){
mat ret;
for(ll i = 0;i <= maxn-1;i++)
for(ll j = 0;j <= maxn-1;j++)
ret.m[i][j] = 0;
for(ll i = 0;i <= maxn-1;i++){
for(ll k = 0;k <= maxn-1;k++){
if(a.m[i][k]){
for(ll j = 0;j <= maxn-1;j++)
ret.m[i][j] += a.m[i][k]*b.m[k][j];
}
}
}
return ret;
}
mat pow_mat(mat a,ll n){
if(n == 1) return a;
mat ret;
ret.unit();
while(n){
if(n&1) ret = ret*a;
a = a*a;
n >>= 1;
}
return ret;
}
int main(){
char ch[10];
while(~scanf("%lld%lld%lld",&N,&M,&K)&&N+M+K){
mat a;
a.unit();
while(K--){
scanf("%s",&ch);
if(ch[0] == 'g'){
scanf("%lld",&x);
a.m[0][x]++;
}
else if(ch[0] == 'e'){
scanf("%lld",&x);
for(int j = 0;j <= maxn-1;j++)
a.m[j][x] = 0;
}
else if(ch[0] == 's'){
scanf("%lld%lld",&x,&y);
if(x == y) continue;
for(int j = 0;j <= maxn-1;j++)
swap(a.m[j][x],a.m[j][y]);
}
}
if(M == 0){
for(int j = 1;j <= N;j++)
printf("0%c",j == N?'\n':' ');
}
else{
a = pow_mat(a,M);
for(int j = 1;j <= N;j++)
printf("%lld%c",a.m[0][j],j == N?'\n':' ');
}
}
return 0;
}