Training little cats(poj3735,矩阵快速幂)

Training little cats

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 10737   Accepted: 2563

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

【题意】:有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 3:
              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。
              具体实现可以看下面的代码。





转载请注明出处:寻找&星空の孩子

题目链接:http://poj.org/problem?id=3735
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL __int64
#define mmax 105


struct matrix
{
    LL mat[mmax][mmax];

    void zero()
    {
        memset(mat,0,sizeof(mat));
    }
    void unit()
    {
        for(int i=0; i<mmax; i++)
            for(int j=0; j<mmax; j++)
                mat[i][j]=(i==j);
    }

} A,T; //A = 初始矩阵 ,T = 转置矩阵

int N;

matrix multiply(matrix a,matrix b)
{
    matrix c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=0; i<N+1; i++)
    {
        for(int j=0; j<N+1; j++)
        {
            if(a.mat[i][j]==0)continue;
            for(int k=0; k<N+1; k++)
            {
                if(b.mat[j][k]==0)continue;
                c.mat[i][k]+=a.mat[i][j]*b.mat[j][k];

            }
        }
    }
    return c;
}

matrix quicklymod(matrix a,LL n)
{
    matrix res;
    for(int i=0; i<105; i++) //单位阵
        for(int j=0; j<105; j++)
            res.mat[i][j]=(i==j);
    while(n)
    {
        if(n&1)
            res=multiply(a,res);
        a=multiply(a,a);
        n>>=1;
    }
    return res;
}

int main()
{
    LL m,k;
    int x,y;
    char ch[5];
    while(scanf("%d%I64d%I64d",&N,&m,&k)!=EOF)
    {
        if(!N&&!m&&!k)break;
 /*       A.zero();
        A.mat[0][0]=1;*/
        T.unit();
        for(int i=0; i<k; i++)
        {
            scanf("%s",ch);
            if(ch[0]=='g')
            {
                scanf("%d",&x);
                T.mat[0][x]++;
            }
            else if(ch[0]=='e')
            {
                scanf("%d",&x);
                for(int i=0; i<=N; i++) T.mat[i][x]=0;
            }
            else if(ch[0]=='s')
            {
                scanf("%d%d",&x,&y);
                for(int i=0; i<=N; i++)
                {
                    swap(T.mat[i][x],T.mat[i][y]);//交换列
                }
            }
        }


        T=quicklymod(T,m);

 /*       matrix ans = A * (T ^ m);
        for(int i = 1; i <= n; i++) printf("%lld ", ans.val[0][i]);
        printf("\n");*/
        printf("%I64d",T.mat[0][1]);
        for(int i=2;i<=N;i++) printf(" %I64d",T.mat[0][i]);
        printf("\n");
    }
    return 0;
}

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

 

 

加油!少年!!!

posted @ 2015-03-17 20:10  寻找&星空の孩子  阅读(419)  评论(0编辑  收藏  举报