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;
}
posted @ 2017-08-17 16:11  小蒟蒻ysn  阅读(160)  评论(0编辑  收藏  举报