Fy's dota2
Fy 觉得自己玩 cf,lol 这种高端游戏已经够厉害了,于
是他决定去玩 dota2.结果 fy 的鼠标右键坏了,所以他就等
到 2250 买了把闪烁匕首,用跳刀前进,准备去送泉水。但
是 fy 一次最多前进 k 的距离,泉水离 fy 现在的距离是 n。
Fy 想知道他到泉水的方案数。
输入格式:
第一行 2 个整数:k,n
输出格式:
一行 1 个整数:代表答案对 7777777 取膜的结果
样例输入:
2 4
样例输出
5
样例解释
一共有 5 种方案
→1→2→3→4
→2→3→4
→2→4
→1→3→4
→1→2→4
数据范围
对于 30%的数据:n<=1000,k<=10
对于 100%的数据:1<=n<=2^31-1,1<=k<=10
【题解】
递推:Ans[i]表示到i位置的方案数,Ans[i]+=Ans[j] ( j < i &&j>=i-k&&j>=1)
再用矩阵快速幂优化下一维的递推。有点类似于斐波那契数列、上台阶等经典。(然而考试时我不记得矩乘怎么弄了。。。)
矩阵乘法
矩阵就是一个二维数组,只有m*p的矩阵A可以和p*n的矩阵B相乘,交换律不可用。
相乘结果为一个m*n的矩阵C,第i列第j行的元素等于A中第i列(p个元素)和B中第j列(p个元素)对应相乘的积之和。
用矩阵乘法,一般这样:
用S表示状态矩阵,T表示转移矩阵,S*T=S’,就是下一状态矩阵。
我们需要根据S和S’,自行构造T转移矩阵。
以此题cell为例:
我们假设第b天的细胞数为a,那个下一个状态就是:第b+1天的细胞数为ax+b+1
我们把a,b填入S,ax+b,b+1填入S’,有
S(a,b)*T( )=S’(ax+b+1,b+1)
但这样的话1这个常数项构造不出来,所以我们加一项:
S(a,b,1)*T( )=S’(ax+b+1,b+1,1)
用我们的聪明才智构造T:
x,0,0
S(a,b,1)*T 1,1,0 =S’(ax+b+1,b+1,1)
1,1,1
我们要计算是n天后的细胞数,S初始化为(0,0,1),意思是第0天有0个细胞
答案就在S*T^n中,那么因为矩阵乘法满足结合律,所以我们可以对T^n进行快速幂。
或者也可以写成
x,0,0
S(a,b,1)*T 1,1,0 =S’(ax+b,b+1,1) 然后初始化(0,1,1),意思是第1天操作前有0个细胞
0,1,1
这样表示的是第b天进行操作(放入细胞、细胞分裂)之前有细胞个数a。
上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=7777777;
int n,k;
struct matrix
{
int a[15][15];
matrix()
{
memset(a,0,sizeof(a));
}
int *operator [](int x)
{
return a[x];
}
matrix operator *(matrix &b)
{
matrix ans;
for (int i=0;i<k;i++)
for (int j=0;j<k;j++)
for (int l=0;l<k;l++)
ans[i][l]=(ans[i][l]+1ll*a[i][j]*b[j][l])%mod;
return ans;
}
}S,T;
void init()
{
for (int i=0;i<k-1;i++) T[i+1][i]=1;
for (int i=0;i<k;i++) T[i][k-1]=1;
}
int main()
{
freopen("fyfy.in","r",stdin);
freopen("fyfy.out","w",stdout);
scanf("%d %d",&k,&n);
S[0][0]=1;
for (int i=1;i<k;i++)
for (int j=0;j<i;j++)
S[0][i]+=S[0][j];
if (n<k)
{
printf("%d",S[0][n]);
return 0;
}
n-=k-1;
init();
while (n)
{
if (n&1) S=S*T;
T=T*T;
n>>=1;
}
printf("%d\n",S[0][k-1]);
return 0;
}