• 洛谷P3990 [SHOI2013]超级跳马


    题意

    题目链接

    分析

    DP部分

    这题乍一看是个水题...

    可以很快看出(DP)的做法

    (dp[i][j])表示到了第(i)行第(j)列的方案数

    那么(dp)转移方程很好写了:

    [dp[i][j]=dp[i-1][j]+dp[i-1][j]+dp[i-1][j+1]+dp[i-2][j] ]

    可能唯一的难点在于这个第四项吧...我这里解释一下这个方程的含义

    我们当这个位置可能由前一列的上一个,前一列的这个,前一列的下一个一步到达(因为限制就是每次只能移动一行或者不动)

    同时我们考虑一步到达这个位置的其它点,容易发现这些点都是可以一步到达((i-2,j))的,所以我们把剩下的点的贡献相当于全部都算在了((i-2,j))上面,而没有剩下的点一定存在于前面三项加上的贡献当中

    然后这个题的(O(nm))算法就这样做出来了

    矩阵快速幂

    但是我们看到数据范围时才知这题并不简单...暗藏杀机...

    (mleq 10^9) !!!

    但是我们观察一下上面的(DP)转移方程,我们可以发现:对于每一列来说,当前这一项只会被前一项和前两项统计到

    所以我们可以考虑 状压 矩阵快速幂优化(DP)

    我们矩阵要记录的就是上一行和上两行的状态,共(100*100)

    然后构造的状态转移矩阵就按照(DP)方程那样把初始矩阵赋一下值就可以了,最后答案也不难得出,见代码即可

    时间复杂度(O(nlogm*(2n)^3)),足以通过此题

    代码

    窝是看了这位巨佬的博客才学会的,如有雷同只是看了就感觉写的都一样了QWQ

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
    	x=0;char ch=getchar();bool f=false;
    	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?-x:x;
    	return ;
    }
    template <typename T>
    inline void write(T x){
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    const int N=105,MOD=30011;
    int n,m;
    #define inc(a,b) (a+b>=MOD?a+b-MOD:a+b)
    struct Matrix{
    	int a[N][N];
    	Matrix(){memset(a,0,sizeof(a));}
    	Matrix operator *(Matrix B){
    		Matrix C;
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=n;j++){
    				for(int k=1;k<=n;k++){
    					C.a[i][j]=inc(C.a[i][j],a[i][k]*B.a[k][j]%MOD);
    				}
    			}
    		}
    		return C;
    	}
    };
    Matrix QuickPow(Matrix A,int y){
    	Matrix res;
    	for(int i=1;i<=n;i++) res.a[i][i]=1;
    	while(y){
    		if(y&1) res=res*A;
    		A=A*A;
    		y>>=1;
    	}
    	return res;
    }
    Matrix base;
    int ans1,ans2;
    int main(){
    	read(n),read(m);
    	if(m<=2){
    		if(n<=2&&m<=n) puts("1");
    		else puts("0");
    		return 0;
    	}
    	for(int i=1;i<=n;i++){
            base.a[i][i-1]=base.a[i][i]=base.a[i][i+n]=base.a[i+n][i]=1;
            if(i!=n)base.a[i][i+1]=1;
        }
        n<<=1;
        base=QuickPow(base,m-2);
        n>>=1;
        if(n==1){
        	write(base.a[1][1]);
        	return 0;
    	}
    	int n2=n<<1;
    	ans1=(base.a[1][n2-1]+base.a[2][n2-1]+base.a[n+1][n2-1])%MOD,ans2=(base.a[1][n2]+base.a[2][n2]+base.a[n+1][n2])%MOD;
    	write(inc(ans1,ans2)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    制作一个Frame用于保存文件和打开文件
    制作一个Frame界面显示盘符的东西
    制作一个图形化界面(演示键盘和鼠标的监听)
    编码解码练习---截取字节
    编码解码问题
    一些在内存里面操作的流
    类型流DataInputstream 和DataOutputStream的用法
    管道流 PipedInputStream和PipedOutputStream 用法
    RandomAccessFile的用法
    面向对象的更多说明
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14093656.html
Copyright © 2020-2023  润新知