1. 程式人生 > >poj 3735 Training little cats 構造矩陣+稀疏矩陣加速連乘+矩陣快速冪

poj 3735 Training little cats 構造矩陣+稀疏矩陣加速連乘+矩陣快速冪

題面描述:

Training little cats
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 13366 Accepted: 3293

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只喵咪分配花生,共有k個分配的操作,重複著k次操作m次,由於m比較大,所以要藉助於矩陣連乘來求解。

題目分析:

由於明確使用矩陣快速冪求解,所以,分析題目可得由一個1*n的矩陣和多個矩陣x相乘之後變為一個1*n的矩陣,那麼這個矩陣x一定是n*n的,那麼就要去構造這個矩陣,構造矩陣的方法如下:

<轉>

解題思路:
我們把剛才那3只貓看做一個矩陣{a,b,c},分別代表他們有的花生個數,顯然初始是{0,0,0}

當進行s操作的時候,我們將初始矩陣乘上一個矩陣,得到的那個矩陣最好也是1行3列的。
那肯定我們要構造的那個矩陣式3*3的矩陣

s 1 2交換操作就是{a,b,c}*x={b,a,c}

x={0 1 0}

     1 0 0

       0 0 1

那麼S操作就是這樣的,首先將X看做一個單位矩陣,要交換哪兩個,只需要交換他們的列就可以了

對於e操作近似於s操作,將e 2舉例:{a,b,c}*x={a,0,c}

x={1 0 0}

      0 0 0

      0 0 1

即將某列置於0

現在問題來了,怎麼構造g操作的矩陣。使下面這個等式成立
 g 1操作  {a,b,c}*X={a+1,b,c}
 g 2操作  {a,b,c}*X={a,b+1,c}
 g 3操作  {a,b,c}*X={a,b,c+1}

這樣所構造的矩陣會含有a,b,c的值,不可能將構造一個這樣的矩陣,這不科學,因為在前面構造s操作與e操作時,我們並不需要知道a,b,c的值

那這樣,我們不妨再{a,b,c}矩陣多加一個1,這樣我們就能實現我們的+1操作了
  要使g 1操作實現{a,b,c,1}*x={a+1,b,c,1}

那麼x= 1 0 0 0

             0 1 0 0

             0 0 1 0

             1 0 0 1

當然這樣構造矩陣,這樣並不影響我們前面的s與e操作

這樣一系列操作之後:

而重複m次那麼多操作,只是需要將{0,0,0,1}* X^m={a,b,c,1}
這個時候a,b,c就是我們要的答案,構造X次方只能用模擬去構造,因為操作最多隻有100次,所有不費什麼時間,現在重要的是X^m

正常的矩陣連乘程式碼:

mat multi(mat a,mat b)
{
    mat c;
    for(int i=1; i<=n+1; i++)
    {
        for(int j=1; j<=n+1; j++)
        {
            c.m[i][j]=0;
            for(int k=1; k<=n+1; k++)
                c.m[i][j]+=(a.m[i][k]*b.m[k][j]);
            //c.m[i][j]%=mod;
        }
    }
    return c;
}

經過加速的矩陣連乘程式碼:

mat multi(mat a,mat b)
{
    mat c;
    memset(c.m,0,sizeof(c.m));
    for(int i=1; i<=n+1; i++)
    {
        for(int j=1; j<=n+1; j++)
        {
            if(a.m[i][j])///加速演算法,一定要進行初始化,否則沒有效果
            {
                for(int k=1; k<=n+1; k++)
                    c.m[i][k]+=(a.m[i][j]*b.m[j][k]);
            }
        }
    }
    return c;
}


程式碼實現:

程式碼一:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn=120;

long long a[maxn],ans2[maxn];
long long n,mm,k;

struct mat
{
    long long m[maxn][maxn];
};

mat x;

mat I;

mat multi(mat a,mat b)
{
    mat c;
    memset(c.m,0,sizeof(c.m));
    for(int i=1; i<=n+1; i++)
    {
        for(int j=1; j<=n+1; j++)
        {
            if(a.m[i][j])///加速演算法,一定要進行初始化,否則沒有效果
            {
                for(int k=1; k<=n+1; k++)
                    c.m[i][k]+=(a.m[i][j]*b.m[j][k]);
            }
        }
    }
    return c;
}

mat power(mat A,long long k)//矩陣快速冪
{
    mat ans=I,p=A;
    while(k)
    {
        if(k&1)///若k為奇數,
        {
            ans=multi(ans,p);
            k--;
        }
        k>>=1;///k=k/2;
        p=multi(p,p);
    }
    return ans;
}

int main()
{
    while(scanf("%lld%lld%lld",&n,&mm,&k)!=EOF)
    {
        getchar();
        if(n==0 && mm==0 && k==0)
            break;
        memset(a,0,sizeof(a));
        memset(ans2,0,sizeof(ans2));
        memset(x.m,0,sizeof(x.m));
        for(int i=1; i<=n+1; i++)
        {
            x.m[i][i]=1;
            I.m[i][i]=1;
        }
        for(int i=0; i<k; i++)
        {
            char c=getchar();
            if(c=='g')
            {
                int k;
                scanf("%d",&k);
                x.m[n+1][k]++;
            }
            else if(c=='e')
            {
                int k;
                scanf("%d",&k);
                for(int i=1; i<=n+1; i++)
                {
                    x.m[i][k]=0;
                }
            }
            else
            {
                int k1,k2;
                scanf("%d%d",&k1,&k2);
                long long tmp[maxn];
                for(int i=1; i<=n+1; i++)
                {
                    tmp[i]=x.m[i][k1];
                    x.m[i][k1]=x.m[i][k2];
                    x.m[i][k2]=tmp[i];
                }
            }
            getchar();
        }
        /*for(int i=1;i<=n+1;i++)
        {
            for(int j=1;j<=n+1;j++)
            {
                cout<<x.m[i][j]<<" ";
            }
            cout<<endl;
        }*/
        mat ans=power(x,mm);

        /*for(int i=1; i<=n+1; i++)
        {
            for(int j=1; j<=n+1; j++)
            {
                cout<<ans.m[i][j]<<" ";
            }
            cout<<endl;
        }*/

        a[n+1]=1;///此處可以簡化掉
        for(int i=1; i<=n+1; i++)
        {
            long long tmp=0;
            for(int j=1; j<=n+1; j++)
            {
                tmp+=a[j]*ans.m[j][i];
            }
            ans2[i]=tmp;
        }
        printf("%lld",ans2[1]);
        for(int i=2; i<=n; i++)
            printf(" %lld",ans2[i]);
        printf("\n");
    }
    return 0;
}

程式碼二:
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn=110;

__int64 n,mm,k;

struct mat
{
    __int64 m[maxn][maxn];
};

mat x;

mat I;

mat multi(mat a,mat b)
{
    mat c;
    memset(c.m,0,sizeof(c.m));
    for(int i=1; i<=n+1; i++)
    {
        for(int j=1; j<=n+1; j++)
        {
            if(a.m[i][j])
            {
                for(int k=1; k<=n+1; k++)
                {
                    if(b.m[j][k])///也是加速
                        c.m[i][k]+=(a.m[i][j]*b.m[j][k]);
                }
            }
        }
    }
    return c;
}

mat power(mat A,__int64 k)//矩陣快速冪
{
    mat ans=I,p=A;
    while(k)
    {
        if(k&1)///若k為奇數,
        {
            ans=multi(ans,p);
            k--;
        }
        k>>=1;///k=k/2;
        p=multi(p,p);
    }
    return ans;
}

int main()
{
    while(scanf("%I64d%I64d%I64d",&n,&mm,&k)!=EOF)
    {
        getchar();
        if(n==0 && mm==0 && k==0)
            break;
        memset(x.m,0,sizeof(x.m));
        for(int i=1; i<=n+1; i++)
        {
            x.m[i][i]=1;
            I.m[i][i]=1;
        }

        for(int i=0; i<k; i++)
        {
            char c=getchar();
            if(c=='g')
            {
                int k;
                scanf("%d",&k);

                x.m[n+1][k]++;
            }
            else if(c=='e')
            {
                int k;
                scanf("%d",&k);
                for(int i=1; i<=n+1; i++)
                {
                    x.m[i][k]=0;
                }
            }
            else
            {
                int k1,k2;
                scanf("%d%d",&k1,&k2);

                /*long long tmp[maxn];
                for(int i=1; i<=n+1; i++)
                {
                    tmp[i]=x.m[i][k1];
                    x.m[i][k1]=x.m[i][k2];
                    x.m[i][k2]=tmp[i];
                }*/
                for(int i=1;i<=n+1;i++)
                swap(x.m[i][k1],x.m[i][k2]);
            }
            getchar();
        }

        mat ans=power(x,mm);

        printf("%I64d",ans.m[n+1][1]);
        for(int i=2; i<=n; i++)
            printf(" %I64d",ans.m[n+1][i]);
        printf("\n");
    }
    return 0;
}