poj 3735 Training little cats (矩阵快速幂)

 

【题意】:有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<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<string>
#define Min(a,b) a<b?a:b
#define Max(a,b) a>b?a:b
#define CL(a,num) memset(a,num,sizeof(a));
#define maxn  40
#define eps  1e-6
#define inf 9999999
#define mx 1<<60
#define ll   __int64
using namespace std;
ll n,m,k;
struct matrix
{
    ll m[110][110];
    void  clear()
    {
        memset(m,0,sizeof(m));
    }
    void unit()
    {
        clear();                       //错在了这里,没清空
        for(int i = 0; i <= n;i++)
        {
            m[i][i] = 1;
        }
    }
};

matrix a;

void init()
{
    int  i;
   a.clear();
    for(i = 0; i <= n;i++)
    {
        a.m[i][i] = 1;

    }


}

matrix mtmul(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 mtpow(matrix a,int k)
{
    matrix b;
    if(k == 1) return a;
    int mid = k / 2;
    b = mtpow(a,mid);
    b = mtmul(b,b);
    if(k&1)
    {
        b = mtmul(b,a);
    }

    return b;


}
*/

matrix  mtpow(matrix a,ll k)
{
    if(k == 1return a;
    matrix e;
    e.unit();
    while(k)
    {
        if(k&1)
        {
           e = mtmul(e,a);
        }

        k>>=1;
        a = mtmul(a,a);


    }
    return e;
}

int main()
{
      ll i ,j,x,y;
     char c[4] ;
    while(scanf("%I64d%I64d%I64d",&n,&m,&k)!=EOF)
    {   if(n ==0 && m ==0 && k == 0break;
         init();
        for(i = 0; i < k;i++)
        {
            scanf("%s",c);
            if(c[0] == 'g')
            {

                scanf("%I64d",&x);

                a.m[0][x] ++ ;
            }
            if(c[0] == 'e')
            {
                 scanf("%I64d",&x);

                for(j = 0; j <= n;j++)
                     a.m[j][x] = 0;
            }
            if(c[0] == 's')
            {
                scanf("%I64d%I64d",&x,&y);
                  if(x == y) continue ;
                for(j = 0; j <= n;j++)
                {
                    swap(a.m[j][x],a.m[j][y]) ;
                }
            }
        }

        if(m == 0)
        {
            for(i = 0; i < n;i++)
            if(i == 0)printf("0");
            else printf(" 0");

            printf("\n");

            continue ;
        }

        a = mtpow(a,m);

        for(i = 1;i <= n;i++)
        {
            if(i == 1 )printf("%I64d",a.m[0][i]);
            else  printf(" %I64d",a.m[0][i]);
        }
        printf("\n");
    }

}

   

       

             

原文地址:https://www.cnblogs.com/acSzz/p/2648087.html