暑期训练赛(6)D

Training little cats
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 9914 Accepted: 2364

Description

Facer's pet cat just gave birth to a brood of little cats. Having considered the health of those lovely cats, Facer decides to make the cats to do some exercises. Facer has well designed a set of moves for his cats. He is now asking you to supervise the cats to do his exercises. Facer's great exercise for cats contains three different moves:
g i : Let the ith cat take a peanut.
e i : Let the ith cat eat all peanuts it have.
s i j : Let the ith cat and jth cat exchange their peanuts.
All the cats perform a sequence of these moves and must repeat it m times! Poor cats! Only Facer can come up with such embarrassing idea. 
You have to determine the final number of peanuts each cat have, and directly give them the exact quantity in order to save them.

Input

The input file consists of multiple test cases, ending with three zeroes "0 0 0". For each test case, three integers nm and k are given firstly, where n is the number of cats and k is the length of the move sequence. The following k lines describe the sequence.
(m≤1,000,000,000, n≤100, k≤100)

Output

For each test case, output n numbers in a single line, representing the numbers of peanuts the cats have.

Sample Input

3 1 6
g 1
g 2
g 2
s 1 2
g 3
e 2
0 0 0

Sample Output

2 0 1

Source

//AC代码
/*
【题意】:有n只猫咪,开始时每只猫咪有花生0颗,现有一组操作,由下面三个中的k个操作组成:
               1. g i 给i只猫咪一颗花生米
               2. e i 让第i只猫咪吃掉它拥有的所有花生米
               3. s i j 将猫咪i与猫咪j的拥有的花生米交换

               现将上述一组操作做m次后,问每只猫咪有多少颗花生?


【题解】:m达到10^9,显然不能直接算。
              因为k个操作给出之后就是固定的,所以想到用矩阵,矩阵快速幂可以把时间复杂度降到O(logm)。问题转化为如何构造转置矩阵?
              说下我的思路,观察以上三种操作,发现第二,三种操作比较容易处理,重点落在第一种操作上。
              有一个很好的办法就是添加一个辅助,使初始矩阵变为一个n+1元组,编号为0到n,下面以3个猫为例:
              定义初始矩阵A = [1 0 0 0],0号元素固定为1,1~n分别为对应的猫所拥有的花生数。
              对于第一种操作g i,我们在单位矩阵基础上使Mat[0][i]变为1,例如g 1:
              1 1 0 0
              0 1 0 0
              0 0 1 0
              0 0 0 1,显然[1 0 0 0]*Mat = [1 1 0 0]
              对于第二种操作e i,我们在单位矩阵基础使Mat[i][i] = 0,例如e 2:
              1 0 0 0
              0 1 0 0
              0 0 0 0
              0 0 0 1, 显然[1 2 3 4]*Mat = [1 2 0 4]
              对于第三种操作s i j,我们在单位矩阵基础上使第i列与第j互换,例如s 1 2:
              1 0 0 0
              0 0 0 1
              0 0 1 0
              0 1 0 0,显然[1 2 0 4]*Mat = [1 4 0 2]
              现在,对于每一个操作我们都可以得到一个转置矩阵,把k个操作的矩阵相乘我们可以得到一个新的转置矩阵T。
              A * T 表示我们经过一组操作,类似我们可以得到经过m组操作的矩阵为 A * T ^ m,最终矩阵的[0][1~n]即为答案。

              上述的做法比较直观,但是实现过于麻烦,因为要构造k个不同矩阵。

              有没有别的方法可以直接构造转置矩阵T?答案是肯定的。
              我们还是以单位矩阵为基础:
              对于第一种操作g i,我们使Mat[0][i] = Mat[0][i] + 1;
              对于第二种操作e i,我们使矩阵的第i列清零;
              对于第三种操作s i j,我们使第i列与第j列互换。
              这样实现的话,我们始终在处理一个矩阵,免去构造k个矩阵的麻烦。

              至此,构造转置矩阵T就完成了,接下来只需用矩阵快速幂求出 A * T ^ m即可,还有一个注意的地方,该题需要用到long long。
              具体实现可以看下面的代码。

              首先想想 为什么这样是可以的呢?

             其实这道题可一看成 1*n 的矩阵 和n*n 的矩阵相乘(因为 对我们有用的信息只是第一行)

            例如 :  上面的例子,第一次是 {0,0,0,1} * T (最后一个 1 的作用是将    加起来 (模拟一下就明白了) )  一次后 变为 {2,0,1,1}* T
*/
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
#define LL long long
#define IT __int64
#define zero(x) fabs(x)<eps
#define mm(a,b) memset(a,b,sizeof(a))
const int INF=0x7fffffff;
const double inf=1e8;
const double eps=1e-10;
const double PI=acos(-1.0);
const int Max=111;
LL n,m,k;
using namespace std;
typedef struct MaTrix
{
    LL m[Max][Max];
    void  clear()
    {
        mm(m,0);
    }
    void Init()
    {
        clear();
        for(int i = 0; i <= n; i++)
        {
            m[i][i] = 1;
        }
    }
}Matrix;
Matrix MT;
void init()
{
    int  i;
    MT.clear();
    for(i = 0; i <= n; i++)
    {
        MT.m[i][i] = 1;
    }
}
Matrix MxMuL(Matrix a,Matrix b)
{
    int i,j,k;
    Matrix c;
    c.clear();
    for(i =0 ; i <= n; i++)
    {
        for(k = 0; k <=n; k++)
        {

            if(a.m[i][k])
                for(j = 0 ; j <=n; j++)
                {
                    c.m[i][j] += a.m[i][k]*b.m[k][j];
                }
        }
    }
    return c;
}
Matrix  MxPow(Matrix a,LL k)
{
    if(k == 1)
        return a;
    Matrix e;
    e.Init();
    while(k)
    {
        if(k&1)
        {
            e = MxMuL(e,a);
        }

        k>>=1;
        a = MxMuL(a,a);
    }
    return e;
}

int main()
{
    LL i ,j,x,y;
    char str[4] ;
    while(cin>>n>>m>>k)
    {
        if(n ==0 && m ==0 && k == 0)
            break;
        init();
        for(i = 0; i < k; i++)
        {
            cin>>str;
            if(str[0] == 'g')
            {
                cin>>x;
                MT.m[0][x]++ ;
            }
            if(str[0] == 'e')
            {
                cin>>x;
                for(j = 0; j <= n; j++)
                    MT.m[j][x] = 0;
            }
            if(str[0] == 's')
            {
                cin>>x>>y;
                if(x == y)
                    continue ;
                for(j = 0; j <= n; j++)
                {
                    swap(MT.m[j][x],MT.m[j][y]) ;
                }
            }
        }
        if(m == 0)
        {
            for(i = 0; i < n; i++)
                if(i == 0)
                    cout<<0;
                else
                    cout<<" "<<0;
            cout<<endl;
            continue ;
        }
        MT = MxPow(MT,m);
        for(i = 1; i <= n; i++)
        {
            if(i == 1 )
                cout<<MT.m[0][i];
            else
                cout<<" "<<MT.m[0][i];
        }
        cout<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值