题解 V1067 【Warcraft III 守望者的烦恼】
Warcraft III 守望者的烦恼
题目大意:
现在有 \(n\) 个格子,一次可以跳过 \(k\) 个格子,问跳完所有格子的方案数。
solution:
首先我们想到一个递推:
设 \(f_{i}\) 为到第 \(i\) 个格子的方案数,那么有:
这是个加法原理。
如果朴素去算是 \(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\) 的矩阵:
做矩阵快速幂即可。
细节处理:
- 我们需要初始化一下,即 \(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;
}