View on GitHub

Wenchong Huang

旧博客,文章搬迁完后删除

返回首页 返回专题

【SHOI2013】超级跳马 题解

设F[i,j]表示第i行第j列的方案数,可以得到F[i,j]=Σ{F[i,j-2k+1]+F[i-1,j-2k+1]+F[i+1,j-2k+1]}(k=1~j/2)

如果枚举i,j,并按上式更新答案,那么时间复杂度O(nm²),可以拿10分

发现可以利用前缀和优化这个式子。

设S[i,j]表示Σ{F[i,j-2k+1]}{k=1~j/2},那么可以推出S[i,j]=S[i,j-2]+S[i,j-1]+S[i-1,j-1]+S[i+1,j-1]

枚举i,j,按上式更新答案,时间复杂度O(nm),可以拿50分

发现数据范围非常诡异,n很小,m很大

我们将S数组画出图示,看一下对S[i,j]产生贡献的点有什么分布规律

发现对S[i,j]有影响的位置都在[i,j]的左边两列,假如我们把每两列设计为一个状态,不就可以用矩阵乘法来往右递推了吗

以n=4为例,S的矩阵递推表达式如下:

这样就可以矩阵快速幂往右推,时间复杂度O(n³log m)

但是当n不同的时候,我们总不可能手动打表转移矩阵吧,我们应该发现这个矩阵的普遍规律

作两条辅助线,将矩阵划分成4块:

发现左上块全是0,右上、左下块的左上-右下对角线为1,右下块则是左上-右下对角线及其左右两格均为1

利用这个规律就可以构造出更大的矩阵了

#include<bits/stdc++.h>
#define Mod 30011
using namespace std;

struct Matrix
{
	int m,n;
	int a[110][110];
} a,b,c;

Matrix operator * (const Matrix &a,const Matrix &b)
{
	Matrix c;
	c.m=a.m;c.n=b.n;
	for(int i=1;i<=a.m;i++)
		for(int j=1;j<=b.n;j++)
		{
			c.a[i][j]=0;
			for(int k=1;k<=a.n;k++)
				c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%Mod;
		}
	return c;
}

Matrix operator ^ (Matrix a,int b)
{
	Matrix ans=a;b--;
	while(b)
	{
		if(b&1) ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}

int main()
{
	int n,m;
	cin>>n>>m;
	b.m=2*n;b.n=1;
	b.a[1][1]=b.a[n+1][1]=b.a[n+2][1]=1;
	a.m=a.n=2*n;
	for(int i=1;i<=n;i++) a.a[i][n+i]=1;
	for(int i=n+1;i<=2*n;i++) a.a[i][i-n]=1;
	a.a[n+1][n+1]=a.a[n+1][n+2]=1;
	for(int i=n+2;i<2*n;i++)
		a.a[i][i]=a.a[i][i-1]=a.a[i][i+1]=1;
	a.a[2*n][2*n]=a.a[2*n][2*n-1]=1;
	c=a^(m-3);
	b=c*b;
	int ans1=b.a[n][1];
	b=a*b;
	cout<<(b.a[2*n][1]-ans1+Mod)%Mod<<endl;
	return 0;
}