题解 V1067 【Warcraft III 守望者的烦恼】


Warcraft III 守望者的烦恼

题目大意:

现在有 \(n\) 个格子,一次可以跳过 \(k\) 个格子,问跳完所有格子的方案数。

solution:

首先我们想到一个递推:
\(f_{i}\) 为到第 \(i\) 个格子的方案数,那么有:

\[f_{i}=f_{i-1}+f_{i-2}+...+f_{i-k} \]

这是个加法原理。

如果朴素去算是 \(O(nk)\) 的,复杂度爆炸。所以我们考虑优化这个递推。

我们发现这个柿子都是按照一定规律加的,即可以用上柿子表达出所有 \(i\) 的取值构成的柿子。所以考虑矩阵优化。

设:

初始矩阵\(\begin{bmatrix} f_{i-1} & f_{i-2} & f_{i-3} \,\,\,\,... & f_{i-k+1} &f_{i-k} \end{bmatrix}\)

目标矩阵\(\begin{bmatrix} f_{i} & f_{i-1} & f_{i-2} \,\,\,\,... & f_{i-k} &f_{i-k+1} \end{bmatrix}\)
可构造出一个 \(k\times k\) 的矩阵:

\[\begin{bmatrix} 1 & 1 & 0 & \cdots& 0 \\ 1 & 0 & 1 & \cdots& 0 \\ \vdots & \vdots & \vdots & \ddots & 0 \\ 1 & 0 & 0 & 0 & 1 \\ 1 & 0 & 0 & 0 & 0 \end{bmatrix}\]

做矩阵快速幂即可。

细节处理:

  • 我们需要初始化一下,即 \(f_0\) 的值应为 \(1\) ,在矩阵中即为 \(res_{1,1}=1\)
  • 取模的数挺奇怪的,建议复制粘贴。
代码
#include
#include
using namespace std;
typedef long long LL;
const int K=15,p=7777777;
int k,n;
struct M {
	LL c[K][K];
}a,res;
M operator * (const M &x,const M &y){
	M t;
	memset(t.c,0,sizeof(t.c));
	for(int i=1;i<=k;i++)
		for(int j=1;j<=k;j++)
			for(int o=1;o<=k;o++)
				t.c[i][j]=(t.c[i][j]+x.c[i][o]*y.c[o][j]%p)%p;
	return t;
}
inline void qpow(int b){
	while(b){
		if(b&1) res=res*a;
		a=a*a;
		b>>=1;
	}
	printf("%lld",res.c[1][1]);
}
int main(){
	scanf("%d%d",&k,&n);
	for(int i=1;i<=k;i++)//构造矩阵
		a.c[i][1]=a.c[i][i+1]=1;
	res.c[1][1]=1;//初始化
	qpow(n);
	return 0;
}

End