POJ 3735 Training little cats

题目连接: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值