[置顶] 矩阵快速幂专题(三)

哈哈哈,博主又回来了!这次专题是第三弹也是最后一弹了,这次会对矩阵进行一个小收尾。做完这25道题,我感觉到其实我矩阵学得并不好,还有许多知识点没有学会。后面看情况可能还会继续开矩阵的专题,那应该是几个月以后的事了。从下周开始,应该会先学习一下数论的相关算法!

这次的七道题目(为什么题目越来越少了)主要是针对了矩阵的优化,对于会TLE的和MLE(内存爆了)的矩阵而且这个矩阵又恰好是同构矩阵(同构矩阵是啥?)的话,可以采用一维数组来模拟二维,从而降低复杂度、降低空间。(竟然是罕见的同时降时间和空间的做法,但是条件有点苛刻=、=)。




第一题 Fzu-1692

分析:首先要告知读者题目有错误(坑啊!),题目中的( L*A(i+n-1)%n+R*A(i+1)%n )应该改为( R*A(i+n-1)%n+L*A(i+1)%n )就是把L和R交换一下位置,不明白为什么福州大学不改掉,都这么长的时间了。

对于这种换苹果、换火柴的问题,做的太多,感觉一下子就有思路,而且可以说题目上已经把递推式给出了。

对于A[i]来说,一次变换后A[i']=R*A[(i+n-1)%n]+A[i]+L*A[(i+1)%n]

构造矩阵

[置顶] 矩阵快速幂专题(三)_第1张图片

但是呢,很可惜的是这个算法会超时!我们观察一下矩阵的形式,发现我们构造出来的矩阵有些特殊,每一行与它相邻的一行之间位置关系只差了1个位置(似乎就叫同构矩阵),是一种循环的关系。那么我们根据线性代数的乱七八糟的性质(我不记得了)就可以推到两个同构矩阵相乘得到的矩阵仍然是同构矩阵,那么我们是不是就可以简化运算,只需求结果矩阵的第一行,然后后面的每一行都可以根据前一行与其位置差1的关系,直接推出来,不必一一运算了。

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define maxn 100+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator
#define push_back PB

typedef long long ll;
const double eps = 1e-10;
const double pi  = acos(-1);
const ll mod = 1e9+7; 
const int inf = 0x3f3f3f3f;

ll M;
struct matrix 
{
	int n;
	ll maze[maxn][maxn];
	void init(int n)
	{
		this->n=n;
		clr(maze,0);
	}
	matrix operator * (const matrix & rhs)
	{
		matrix ans;
		ans.init(n);
		rep(j,n)
			rep(k,n)
				ans.maze[0][j]=(ans.maze[0][j]+maze[0][k]*rhs.maze[k][j])%M;
		repf(i,1,n-1)
			rep(j,n)
				ans.maze[i][j]=ans.maze[i-1][(j+n-1)%n];
		return ans;
	}
};
matrix qlow(matrix a,ll n)
{
	matrix ans;
	ans.init(a.n);
	rep(i,a.n)ans.maze[i][i]=1;
	while(n)
	{
		if(n&1)ans=ans*a;
		a=a*a;
		n>>=1;
	}
	return ans;
}
int main()
{
	//freopen("d:\\acm\\in.in","r",stdin);
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,L,R;
		ll m;
		scanf("%d %I64d %d %d %lld",&n,&m,&L,&R,&M);
		matrix ans;
		ans.init(n);
		rep(i,n)scanf("%lld",&ans.maze[0][i]);
		matrix ant;
		ant.init(n);
		rep(i,n)
		{
			ant.maze[i][i]=1;
			ant.maze[(i+n-1)%n][i]=R;
			ant.maze[(i+1)%n][i]=L;
		}
		ant=qlow(ant,m);
		ans=ans*ant;
		int flag=0;
		rep(i,n)
		{
			if(flag)putchar(' ');
			flag=1;
			printf("%lld",ans.maze[0][i]);
		}
		putchar('\n');
	}
	return 0;
}



第二题 hdu-4291

分析:这是一道非常有意思的题目,它在构造矩阵上没有什么难度,递推都已经给出了。但是这道题目难在g(n)迭代了三次,在里层是不可以取1e9+7的模的。这里需要使用循环节,循环节通俗的说就是按照斐波那契式的递推(a(n)=b1*a(n-1)+b2*a(n-2)+......)并且取模的话,一定会经历一个循环段然后会回到开头的状态(a(XXX)=a1,a(XXX+1)=a2,a(XXX+3)=a3,.......这里XXX-1就是循环节),至于证明我就不证明了(其实我也不会证明,大家知道就好了,在这种情况下循环节是一定存在的)。那么我们就可以一层一层的求循环节了,每一层使用对应的循环节取模,最外层用1e9+7。

至于为什么正确的话,可以考虑a(XXX)=a1,那么a(XXX%(XXX-1))=a(1)=a1,是不是内层的取模与外层的取模相一致。

这里给出求循环节的代码(其实就是暴力)

ll m = 1e9+7;
//222222224;
//183120
int main()
{
	//freopen("d:\\acm\\in.in","r",stdin);
	ll a=0,b=1;
	ll t;
	for(int i=1;;i++)
	{
		t=(a+3*b)%m;
		a=b;
		b=t;
		if(a==0&&b==1)
		{
			cout<<i<<endl;
			break;
		}
	}
	return 0;
}
//222222224
//183120
//240
下我们得到了四个循环节(240那个是我打着玩的,其实不要)

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define maxn 0+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator
#define push_back PB

typedef long long ll;
const double eps = 1e-10;
const double pi  = acos(-1);
const ll mod = 1e9+7; 
const int inf = 0x3f3f3f3f;

ll m[3]={183120,222222224,1e9+7};
int ca;
struct matrix 
{
    int n;
    ll maze[maxn][maxn];
    void init(int n)
    {
        this->n=n;
        clr(maze,0);
    }
    matrix operator * (const matrix& rhs)
    {
        matrix ans;
        ans.init(n);
        rep(i,n)
            rep(j,n)
                rep(k,n)
                    ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%m[ca];
        return ans;
    }
};
matrix qlow(matrix a,ll n)
{
    matrix ans;
    ans.init(a.n);
    rep(i,a.n)ans.maze[i][i]=1;
    while(n)
    {
        if(n&1)ans=ans*a;
        a=a*a;
        n>>=1;
    }
    return ans;
}
int main()
{
    //freopen("d:\\acm\\in.in","r",stdin);
    ll n;
    while(scanf("%I64d",&n)!=EOF)
    {
        n%=240;
        for(ca=0;ca<3;ca++)
        {
            if(n<2)break;
            matrix ans;
            ans.init(2);
            ans.maze[1][0]=ans.maze[0][1]=1;
            ans.maze[1][1]=3;
            ans=qlow(ans,n-1);
            n=ans.maze[1][1];
        }
        printf("%I64d\n",n);
    }
    return 0;
}

其实我们可以看出来这个最里层的n其实是以240为循环(只有240种不同的结果)这么少的结果,正好打表!

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define maxn 0+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator
#define push_back PB

typedef long long ll;
const double eps = 1e-10;
const double pi  = acos(-1);
const ll mod = 1e9+7; 
const int inf = 0x3f3f3f3f;

int dat[240]={0,1,42837,301657676,293732885,490640373,33742025,164692016,462308703,52762375,343131756,21405314,237068195,596899309,906139148,211536041,366655491,896628168,66227266,72120020,402845524,142832152,798631844,624391292,206125778,943175270,640019193,303259648,182635905,579125043,579433543,83927026,717557117,40368430,863093688,335922744,512468934,813575940,844062243,707187631,278519020,670147186,933772741,378841811,627788085,258191260,844841005,802471932,662733064,781240855,676645643,236653810,511827197,880889921,799930429,42383237,938628882,679827669,909921493,863143716,347415371,427513662,372282604,576747399,938628882,839037397,33742025,420342938,835920182,193194645,57151287,416852020,337266943,547930942,487542115,764567815,676186842,326357106,772837852,284929959,278519020,473357016,682900754,316947814,153532664,804007392,859774430,848575784,717557117,50843067,579433543,857877224,164332999,157021226,414571236,568476330,793874229,136594603,693461119,491013304,138865244,804171493,227162155,610297485,366655491,248827358,293577661,347255110,998948656,859995852,375475523,809290428,462308703,118039786,799930429,339244061,247992086,78659144,503609964,396857679,0,396857679,503609964,78659144,247992086,339244061,200069578,118039786,462308703,809290428,375475523,859995852,1051351,347255110,293577661,248827358,366655491,610297485,772837852,804171493,138865244,491013304,693461119,136594603,206125778,568476330,414571236,157021226,164332999,857877224,420566464,50843067,717557117,848575784,859774430,804007392,846467343,316947814,682900754,473357016,278519020,284929959,227162155,326357106,676186842,764567815,487542115,547930942,662733064,416852020,57151287,193194645,835920182,420342938,966257982,839037397,938628882,576747399,372282604,427513662,652584636,863143716,909921493,679827669,938628882,42383237,200069578,880889921,511827197,236653810,676645643,781240855,337266943,802471932,844841005,258191260,627788085,378841811,66227266,670147186,278519020,707187631,844062243,813575940,487531073,335922744,863093688,40368430,717557117,83927026,420566464,579125043,182635905,303259648,640019193,943175270,793874229,624391292,798631844,142832152,402845524,72120020,933772741,896628168,366655491,211536041,906139148,596899309,762931812,21405314,343131756,52762375,462308703,164692016,966257982,490640373,293732885,301657676,42837,1};
int main()
{
    //freopen("d:\\acm\\in.in","r",stdin);
    ll n;
    while(~scanf("%lld",&n))
    {
        cout<<dat[n%240]<<endl;
    }
    return 0;
}




第三题 Uva-12470

分析:矩阵裸题,非常简单,题目已经给出了递推式,这里仅给出代码

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#define maxn 0+5
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const double pi = acos( -1 );
const ll mod = 1e9+9;
const double eps = 1e-10;

ll mul(ll a,ll b)
{
	ll ans=0;
	while(b)
	{
		if(b&1)ans=(ans+a)%mod;
		a=(a+a)%mod;
		b>>=1;
	}
	return ans;
}
struct matrix
{
	int n;
	ll maze[maxn][maxn];
	void init(int n)
	{
		this->n=n;
		clr(maze,0);
	}
	matrix operator * (const matrix & rhs)
	{
		matrix ans;
		ans.init(n);
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				for(int k=0;k<n;k++)
					ans.maze[i][j]=(ans.maze[i][j]+mul(maze[i][k],rhs.maze[k][j]))%mod;
		return ans;
	}
};
matrix qlow(matrix a,ll n)
{
	matrix ans;
	ans.init(a.n);
	for(int i=0;i<a.n;i++)ans.maze[i][i]=1;
	while(n)
	{
		if(n&1)ans=ans*a;
		a=a*a;
		n>>=1;
	}
	return ans;
}
int main()
{
    //freopen("d:\\acm\\in.in","r",stdin);
	ll n;
	while(scanf("%lld",&n),n)
	{
		if(n<4)
		{
			printf("%lld\n",n-1);
			continue;
		}
		matrix ans;
		ans.init(3);
		ans.maze[0][1]=1;
		ans.maze[0][2]=2;
		matrix ant;
		ant.init(3);
		ant.maze[0][2]=ant.maze[1][0]=ant.maze[1][2]=ant.maze[2][1]=ant.maze[2][2]=1;
		ant=qlow(ant,n-3);
		ans=ans*ant;
		printf("%lld\n",ans.maze[0][2]);
	}
    return 0;
}




第四题 hdu-2855

分析:非常奇怪的一道题目,需要很大的脑洞。有些人通过打表查oeis发现了规律F(n)=3*F(n-1)-F(n-2),然后就可以愉快地矩阵快速幂了(其实这也不失为一种好方法,但是比赛的时候没有oeis怎么办,那只能从打表的前几个直接看出规律,说实话有点难啊)。

但是呢,还是有好办法的。从题意上看,可以看出组合数满足二项式定理的系数,那么我们看能不能凑到二项式定理上。考虑f(n)是否可以转化为A^n的形式,我们想起来以前构造过的求斐波那契的矩阵A=[ 1  1  | (换行) 1   0 ],看出可以用A^n来代替f(n),那么还有一项系数怎么办呢,不妨用E^n(E是单位矩阵)来填补空缺。那么题目就转化为了(A+E)^n,我们需要的答案就是结果矩阵的[0][1]或者[1][0]项。

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define maxn 0+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator
#define push_back PB

typedef long long ll;
const double eps = 1e-10;
const double pi  = acos(-1);
const ll mod = 1e9+7; 
const int inf = 0x3f3f3f3f;

ll m;
struct matrix
{
    int n;
    ll maze[maxn][maxn];
    void init(int n)
    {
        this->n=n;
        clr(maze,0);
    }
    matrix operator *(const matrix& rhs)
    {
        matrix ans;
        ans.init(n);
        rep(i,n)
            rep(j,n)
                rep(k,n)
                    ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%m;
        return ans;
    }
};
matrix qlow(matrix a,int n)
{
    matrix ans;
    ans.init(a.n);
    rep(i,a.n)ans.maze[i][i]=1;
    while(n)
    {
        if(n&1)ans=ans*a;
        a=a*a;
        n>>=1;
    }
    return ans;
}
int main()
{
    //freopen("d:\\acm\\in.in","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d %lld",&n,&m);
        matrix ans;
        ans.init(2);
        ans.maze[0][0]=2;
        ans.maze[0][1]=ans.maze[1][0]=ans.maze[1][1]=1;
        ans=qlow(ans,n);
        printf("%lld\n",ans.maze[0][1]);
    }
    return 0;
}




第五题 hdu-3658

分析:这类题目与之前的有些类似,先考虑转化的问题。假设前n个字符都已经符合条件了,我们把最后一个字符作为分类的基础(或者说是标志)。我一开始把以A~Z结尾和以a~z结尾分为两类,互相计算转化,但是在构造矩阵的时候发现不可以,因为A~Z转化到a~z的个数是不确定的,不能构造出固定的矩阵。那么我只能想到把所有字符各自的算作一类,那么矩阵就可以构造了。在写递推式的时候,我又遇到了困难,无法写出完全满足题目两个条件的式子。那么我考虑组合数学中最常见的方法,将所有的减去不满足的(取模对加减法满足分配律),先计算每相邻两个字符差值不大于32的所有种类(可以取模),再计算每相邻两个字符差值小于32的所有种类(可以取模),将前者减去后者,如果是负值的话加上一个mod。


#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define maxn 50+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator
#define push_back PB

typedef long long ll;
const double eps = 1e-10;
const double pi  = acos(-1);
const ll mod = 1e9+7; 
const int inf = 0x3f3f3f3f;

struct matrix 
{
    int n;
    ll maze[maxn][maxn];
    void init(int n)
    {
        this->n=n;
        clr(maze,0);
    }
    matrix operator *(const matrix& rhs)
    {
        matrix ans;
        ans.init(n);
        rep(i,n)
            rep(j,n)
                rep(k,n)
                    ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%mod;
        return ans;
    }
};
matrix qlow(matrix a,int n)
{
    matrix ans;
    ans.init(a.n);
    rep(i,a.n)ans.maze[i][i]=1;
    while(n)
    {
        if(n&1)ans=ans*a;
        a=a*a;
        n>>=1;
    }
    return ans;
}
int main()
{
    //freopen("d:\\acm\\in.in","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int m;
        scanf("%d",&m);
        matrix ans;
        ans.init(52);
        rep(i,52)
        {
            if(i<26)
                rep(j,27+i)
                    ans.maze[j][i]=1;
            else 
                repf(j,i-26,51)
                    ans.maze[j][i]=1;
        }
        ans=qlow(ans,m-1);
        ll anw=0;
        rep(i,52)
            rep(j,52)
                anw=(anw+ans.maze[i][j])%mod;
        ans.init(52);
        rep(i,52)
        {
            if(i<26)
                rep(j,26+i)
                    ans.maze[j][i]=1;
            else 
                repf(j,i-25,51)
                    ans.maze[j][i]=1;
        }
        ans=qlow(ans,m-1);
        ll ant=0;
        rep(i,52)
            rep(j,52)
                ant=(ant+ans.maze[i][j])%mod;
        anw-=ant;
        if(anw<0)anw+=mod;
        printf("%lld\n",anw);
    }
    return 0;
}




第六题  poj-3150

分析:这道题与之前做过的题目非常的相似,没有什么新意。根据n和d很简单的可以构造矩阵,因为d不同那么构造的矩阵不同,所以我就不画了。但是呢,n可以到500,非常大,一个时间复杂度上n^3就可能会TLE,而且内存上面可能会出现问题(我就是,明明感觉500*500的long long一点问题都没有,但是就是各种运行就崩溃)。所以要考虑矩阵的优化,看出这道题目中的矩阵满足循环矩阵的定义(定义是啥),那么我们使用一维数组来代替二维矩阵(前面说过了,循环矩阵每相邻两行之间的特殊关系,可以方便的简化运算,还可以方便的减小存储数据)。写法类似矩阵快速幂,就是把一维数组代替二维的进行运算,实在不会写就多写几个全局变量。有一个点需要注意,就是一维数组之间模拟矩阵相乘,之间有一个差量,是往前开始算的,我也讲不清楚,大家画画图,稍微模拟一下就懂了。

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define maxn 500+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator
#define push_back PB

typedef long long ll;
const double eps = 1e-10;
const double pi  = acos(-1);
const ll mod = 1e9+7; 
const int inf = 0x3f3f3f3f;

int n;
ll m;
ll dat[maxn];
ll maze[maxn];
ll anw[maxn];
ll tmp[maxn];

void mul(ll a[],ll b[])
{
	rep(i,n)
	{
		tmp[i]=0;
		rep(j,n)
			tmp[i]=(tmp[i]+a[j]*b[(j+n-i)%n])%m;
	}
	rep(i,n)
		a[i]=tmp[i];
}

void qlow(int n)
{
	clr(anw,0);
	anw[0]=1;
	while(n)
	{
		if(n&1)mul(anw,maze);
		mul(maze,maze);
		n>>=1;
	}
}

int main()
{
	//freopen("d:\\acm\\in.in","r",stdin);
	int d,k;
	while(~scanf("%d %lld %d %d",&n,&m,&d,&k))
	{
		rep(i,n)scanf("%lld",&dat[i]);
		clr(maze,0);
		maze[0]=1;
		repf(i,1,d)maze[i]=maze[n-i]=1;

		qlow(k);
		mul(dat,anw);
		int flag=0;
		rep(i,n)
		{
			if(flag)putchar(' ');
			flag=1;
			printf("%lld",dat[i]);
		}
		putchar('\n');
	}
	return 0;
}




第七题  poj-3735

分析:感觉这道题就是用来测一测你的线性代数好不好,我很不好,所以做了很久。

分别分析三种操作的矩阵,语言讲不清,直接上图,[0][0]位置上是常数位1,a(n)表示第n只猫持有的花生数

对于g i 操作

[置顶] 矩阵快速幂专题(三)_第2张图片

对于s i j 操作

[置顶] 矩阵快速幂专题(三)_第3张图片

对于e i 操作

[置顶] 矩阵快速幂专题(三)_第4张图片


有了三种操作的对应矩阵,我就想把每个操作的构造矩阵都乘起来,那么就可以得到一次操作的矩阵,然后矩阵快速幂不是很简单吗!但是我又错了,这样的话显然是会TLE的,那么就还是要从矩阵的构造上下功夫,然后人家告诉我这些操作其实是什么基本操作(乱七八糟的,完全不记得了),可以直接在一个单位矩阵上面按照操作进行变换。然后,就没有然后了。g i 对应maze[0][i]++, s i j对应把maze的第i列和第j列完全交换,e i 对应把maze的第i列完全清零,最后矩阵快速幂!唉,数学差,真悲伤啊!

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define maxn 100+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator
#define push_back PB

typedef long long ll;
const double eps = 1e-10;
const double pi  = acos(-1);
const ll mod = 1e9+7; 
const int inf = 0x3f3f3f3f;

struct matrix 
{
	int n;
	ll maze[maxn][maxn];
	void init(int n)
	{
		this->n=n;
		clr(maze,0);
	}
	matrix operator* (const matrix& rhs)
	{
		matrix ans;
		ans.init(n);
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				for(int k=0;k<n;k++)
					if(maze[i][k]&&rhs.maze[k][j])
						ans.maze[i][j]+=maze[i][k]*rhs.maze[k][j];
		return ans;
	}
};
matrix qlow(matrix a,int n)
{
	matrix ans;
	ans.init(a.n);
	for(int i=0;i<a.n;i++)ans.maze[i][i]=1;
	while(n)
	{
		if(n&1)ans=ans*a;
		a=a*a;
		n>>=1;
	}
	return ans;
}
int main()
{
	//freopen("d:\\acm\\in.in","r",stdin);
	int n,m,k;
	while(scanf("%d %d %d",&n,&m,&k),n||m||k)
	{
		matrix ans;
		ans.init(n+1);
		for(int i=0;i<n+1;i++)ans.maze[i][i]=1;
		char op[5];
		int a,b;
		while(k--)
		{
			scanf("%s",op);
			if(op[0]=='g')
			{
				scanf("%d",&a);
				ans.maze[0][a]++;
			}
			else if(op[0]=='s')
			{
				scanf("%d %d",&a,&b);
				for(int i=0;i<n+1;i++)
					swap(ans.maze[i][a],ans.maze[i][b]);
			}
			else 
			{
				scanf("%d",&a);
				for(int i=0;i<n+1;i++)
					ans.maze[i][a]=0;
			}
		}
		ans=qlow(ans,m);
		int flag=0;
		for(int i=1;i<n+1;i++)
		{
			if(flag)putchar(' ');
			flag=1;
			printf("%lld",ans.maze[0][i]);
		}
		putchar('\n');
	}
	return 0;
}





你可能感兴趣的:(矩阵,快速幂)