zoukankan      html  css  js  c++  java
  • 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");
        }

    }

       

           

                 

  • 相关阅读:
    Qt 动画之QPropertyAnimation
    QT 文件的读写
    keil编译程序出现declaration may not appear after executable statement in block 错误 解决办法
    QT UDP通信接受发送数据
    QT 样式表
    QT 滑动条和滚动条的样式
    C++调用dll例子
    MFC非模态窗口gdi+自绘图片
    获取windows操作系统分辨率(DPI)
    单个循环输出数圈(简单易懂)
  • 原文地址:https://www.cnblogs.com/acSzz/p/2648087.html
Copyright © 2011-2022 走看看