Warcraft III 守望者的烦恼 Vijos 矩阵快速幂

背景

守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看完所有的监狱,只是从入口进入,然后再从出口出来就算完成任务了。
描述

头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。
守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?
格式

输入格式

第一行是闪烁技能的等级k(1<=k<=10)
第二行是监狱的个数n(1<=n<=2^31-1)
输出格式

由于方案个数会很多,所以输出它 mod 7777777后的结果就行了
样例1

样例输入1

2
4
样例输出1

5
限制

各个测试点1s
提示

把监狱编号1 2 3 4,闪烁技能为2级,
一共有5种方案
→1→2→3→4
→2→3→4
→2→4
→1→3→4
→1→2→4
小提示:建议用int64,否则可能会溢出

这题是群里一朋友发的,看到这题我觉得很有必要练练。

思路:
递推加矩阵快速幂。我们都知道斐波那契数列F[n]=F[n-2]+F[n-1]这题,我们可以根据斐波那契的思路进行递推,我们知道他每次只能闪现1~k即[1,k]都可以选择,所以我们容易得出,当n∈[1,k]时,方法数为2^(n-1)。之后>k的每一个方法数,都是通过前k项之和得出。所以容易写出方程F[n]=F[n-1]+F[n-2]+······+F[n-k],但是这里n的取值比较大,因此不能用O(n)的方式,需要用加速幂求解。

根据矩阵的基本性质,我们只需要根据n之前的k项推出此时的n即可
如假设k=4 我们就需要预处理4个值,然后从第5个开始计算
矩阵
根据图形我们可知,两个矩阵都随着k的变化而变化,第一个矩阵总是1*k第二个总是k*k,因此可以编写代码了,需要注意的是仔细别写错

代码可能写的也不是很好,欢迎提建议

代码:

import java.util.Scanner;
public class _WarcraftIII守望者的烦恼 {
    private static int k;
    private static long n;
    private static final long MOD = 7777777;
    public static void main(String[] args) {
        Scanner sn = new Scanner(System.in);
        k = sn.nextInt();
        n = sn.nextLong();
        long[][] ans = new long[2][k+1];            //放置最终答案
        long[][] matrix = new long[k+1][k+1];       //放置加速的矩阵
        long[][] final_matrix = new long[k+1][k+1]; //放置单位初始矩阵
        ans[1][k] = 1;
        for(int i = 1;i<=k;i++){
            ans[1][k-i] = ans[1][k-i+1]*2;
            final_matrix[i][i] = 1;
            matrix[i][1] = 1;
            if(i<k)matrix[i][i+1] = 1;
        }
        if(n>k){
            n=n-k;
            while(n>0){//加速幂
                if((n&1)==1)
                    final_matrix = matrix_multiply(final_matrix,matrix);
                matrix = matrix_multiply(matrix,matrix);
                n>>=1;
            }
            ans = matrix_multiply(ans,final_matrix);
            System.out.println(ans[1][1]);
        }else{
            System.out.println(ans[1][(int) (k-n+1)]);//如果n<=k那么直接输出预处理的答案
        }
        sn.close();
    }

    private static long[][] matrix_multiply(long[][] final_matrix,long[][] matrix) {
        int len1 = final_matrix.length;
        long[][] temp = new long[len1][k+1];
        for(int i = 1;i<len1;i++){
            for(int j = 1;j<=k;j++){
                for(int z = 1;z<=k;z++)
                    temp[i][j]=(temp[i][j]+final_matrix[i][z]*matrix[z][j])%MOD;
            }
        }
        return temp;
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值