BZOJ 4417: [Shoi2013]超级跳马【矩阵快速幂优化dp

80 篇文章 0 订阅
16 篇文章 0 订阅

矩阵快速幂优化dp……大概似乎还是很裸的……然而我昨天才学会这套理论于是考试只好在200+分场里110滚粗【哭

嗯考虑用f[i][j]表示在第i行第j列的方案总数

转移的话,因为只能跳奇数格,所以f[i][j]=f[i-2][j]+f[i-1][j-1]+f[i-1][j]+f[i-1][j+1](边界啥的判一下就是了

如果不考虑从i-1列转移的方案,那么f[i][j]==f[i-2][j],于是再加上相邻的那三个格子的贡献就好了……

发现第二维足够小,而且只和前两列的结果有关,所以直接用矩阵搞一搞就好


用一个2n*1的矩阵来记录答案,上面n行记录f[i-1],下面n行记录f[i-2],转移的时候把上面一半粘到下面来,f[i]就按照递推式更新就好

当n==3时,转移矩阵差不多是这样的↓(这四块的位置应该随便吧……反正差不多就好23333

1 1 0 1 0 0

1 1 1 0 1 0

0 1 1 0 0 1

1 0 0 0 0 0

0 1 0 0 0 0

0 0 1 0 0 0

#include<bits/stdc++.h>
#define MAXN 105
#define MAXM 1000000057
#define MOD 30011
using namespace std;	int n,m;

struct matrix{
	int d[MAXN][MAXN];
	int x,y;
	matrix(){x=y=0,memset(d,0,sizeof d);}
	matrix(int xx):x(xx),y(xx){
		memset(d,0,sizeof d);
		for(int i=0;i<x;++i)
			d[i][i]=1;
	}
	matrix(int xx,int yy):x(xx),y(yy){memset(d,0,sizeof d);}
	matrix operator * (const matrix ano) {
		matrix TMP(x,ano.y);
		for(int i=0;i<x;++i)
			for(int j=0;j<y;++j)
				for(int k=0;k<ano.y;++k)
					TMP.d[i][k]+=d[i][j]*ano.d[j][k],TMP.d[i][k]%=MOD;
//		TMP.write();
		return TMP;
	}
	matrix pow(long long cf){
		matrix RTN(x),TMP=*this;
		for(;cf;cf>>=1,TMP=TMP*TMP)
			if(cf&1)
				RTN=RTN*TMP;
		return RTN;
	}
	void write(){
		for(int i=0;i<x;++i){
			for(int j=0;j<y;++j)	printf("%d ",d[i][j]);
			puts("");
		}
		puts("");
	}
}A,B,P,F;

int main(){
	scanf("%d%d",&n,&m);
	if(n>m)	return puts("0"),0;
	----m;
	
	A=B=matrix(n<<1,n<<1);
	P=matrix(n<<1,1);
	P.d[0][0]=P.d[1][0]=1;
	if(n==1&&m>=2)	P.d[1][0]=0;
//	P.write();
	for(int i=0;i<n;++i){
		A.d[i][i+n]=A.d[i+n][i]=1;
		for(int j=max(i-1,0);j<=min(i+1,n-1);++j)	A.d[i][j]=1;
	}
	
	B=A.pow(m);
	P=B*P;

//	for(int i=1;i<=m;++i)	P=A*P;
	
	printf("%d",P.d[n-1][0]);
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值