poj 3735 Training little cats

转载:http://www.cppblog.com/y346491470/articles/157284.html

【题意】:有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。

View Code
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
int n , m , k;
class Matrix
{
public:
      LL val[124][124];
      void Zero(){  memset( val , 0 , sizeof( val ) ); }
      void Init( ){
          Zero();
          for( int i = 0 ; i < 124 ; i ++ )
               val[i][i] = 1;        
      }    
      Matrix operator *( const Matrix &b );
}A;
Matrix Matrix::operator *( const Matrix &b ){
      Matrix temp;
      temp.Zero();
      for( int i = 0 ; i <=  n ;  i ++ )
         for( int k = 0 ; k <= n ; k ++ )           
                if( val[i][k] )
                for( int j = 0 ; j <=  n;  j ++ )
                      temp.val[i][j] += val[i][k]*b.val[k][j];                
      return temp;
}
void Solve( ){
    Matrix temp;
    temp.Init();
    while( m ){
        if( m&1 ) temp = temp*A;
        A  = A*A;
        m >>= 1;    
    }
    for( int i = 1; i < n ; i ++ )
         printf( "%I64d ",temp.val[0][i] );
    printf( "%I64d\n",temp.val[0][n] );
         
}
int main( )
{
   char str[3];
   int a,b;
   while( scanf( "%d %d %d",&n,&m,&k ),n||m||k ){
        A.Init();
        for( int i = 0 ; i < k ; i ++ ){
             scanf( "%s",str );
             switch( str[0] ){
                  case 'g':scanf( "%d",&a );A.val[0][a] ++;
                           break;
                  case 's':scanf( "%d %d",&a,&b );
                            for( int i = 0 ; i <= n ; i ++ )
                                swap( A.val[i][a],A.val[i][b] );
                            break;
                  case 'e':scanf( "%d",&a );
                           for( int i = 0 ; i <= n ; i ++ )
                                A.val[i][a] = 0;        
             }    
        }
        Solve();          
   }    
   return 0;    
}

 

posted @ 2012-07-26 09:26  wutaoKeen  阅读(169)  评论(0编辑  收藏  举报