zoukankan      html  css  js  c++  java
  • JZOJ 6873. 【2020.11.19提高组模拟】飞翔的鸟(矩阵乘法)

    JZOJ 6873. 【2020.11.19提高组模拟】飞翔的鸟

    题解

    • n , k n,k n,k的数据范围看起来就知道要用矩乘,但障碍的位置是不确定的。
    • 如果暴力枚举 n − 2 n-2 n2次障碍的位置,分别计算每种情况障碍左右两边的方案数,再左右相乘起来,每种情况相加求平均数,可以通过部分的数据。
    • 这样显然过不了,考虑能否一次做完?
    • 其实可以每个位置设两个状态 0 / 1 0/1 0/1分别表示是否经过障碍,然后分三种情况, 0 0 0转移到 0 0 0 1 1 1转移到 1 1 1 0 0 0转移到 1 1 1
    • 其中前两种可以直接按题意从 x x x转移到 x − 1 , x , x + 1 x-1,x,x+1 x1,x,x+1,而第三种要从障碍左边那列转移到障碍右边,枚举左边 x − 1 , x , x + 1 x-1,x,x+1 x1,x,x+1,右边 x − 1 , x , x + 1 x-1,x,x+1 x1,x,x+1 3 ∗ 3 = 9 3*3=9 33=9种转移,而障碍那列直接跳过,
    • 于是这样最后矩乘做 n − 2 n-2 n2次。
    • 但常数比较大,可能需要优化,矩乘中省去一些没有用的转移即可。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 265
    #define ll unsigned long long
    #define md 1000000007
    int n, m, A, B;
    ll c[N][N], f[N][N], g[N][N];
    ll qpw(ll x, ll y) {
    	if(!y) return 1;
    	ll l = qpw(x, y / 2);
    	if(y % 2) return l * l % md * x % md;
    	return l * l % md;
    }
    void ksm(int n) {
    	if(n == 0) {
    		for(int i = 1; i <= m * 2; i++) f[i][i] = 1;
    	}
    	else {
    		ksm(n / 2);
    		memset(g, 0, sizeof(g));
    		for(int i = 1; i <= m * 2; i++) 
    			for(int j = (i > m) ? (m + 1) : 1; j <= m * 2; j++) 
    				for(int k = (i > m) ? (m + 1) : 1; k <= ((j > m) ? m * 2 : m); k++) {
    					if(k % 18 == 0) g[i][j] %= md;
    					g[i][j] += f[i][k] * f[k][j];
    				}
    		for(int i = 1; i <= m * 2; i++)
    			for(int j = 1; j <= m * 2; j++) f[i][j] = g[i][j] % md;
    		if(n % 2) {
    			memset(g, 0, sizeof(g));
    			for(int i = 1; i <= m * 2; i++) 
    				for(int j = (i > m) ? (m + 1) : 1; j <= m * 2; j++) {
    					int x = j;
    					if(x > m) x -= m;
    					for(int k = max(x - 2, 0); k <= min(x + 2, m * 2); k++) g[i][j] += f[i][k] * c[k][j];
    					x += m;
    					for(int k = max(x - 2, 0); k <= min(x + 2, m * 2); k++) g[i][j] += f[i][k] * c[k][j];
    				}
    			for(int i = 1; i <= m * 2; i++)
    				for(int j = 1; j <= m * 2; j++) f[i][j] = g[i][j] % md;	
    		}
    	}
    }
    int main() {
    	int i, j, k;
    	scanf("%d%d%d%d", &n, &m, &A, &B);
    	for(i = 1; i <= m + m; i++) {
    		if(i == m) c[i][i] = c[i][i - 1] = 1;
    		else if(i == m + 1) c[i][i] = c[i][i + 1] = 1;
    		else c[i][i] = c[i][i + 1] = c[i][i - 1] = 1;
    	}
    	for(i = A + 1; i < B; i++) 
    		for(j = -1; j < 2; j++) if(i + j <= m)
    			for(k = -1; k < 2; k++) if(i + k) c[i + j][i + m + k]++;
    	ksm(n - 2);
    	printf("%lld
    ", f[m / 2][m / 2 * 3] * qpw(n - 2, md - 2) % md);
    	return 0;
    }
    
  • 相关阅读:
    AS3 条件编译
    as3 中trace() 函数对效率的影响
    将as3代码转换成为js代码
    flash图标插件
    STAR面试法
    虚拟机window7与主机之间文件复制设置
    linux虚拟机网络设置好ping百度没有用
    Linux常用命令
    路由器wiff设置
    linux网络启动报错
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279481.html
Copyright © 2011-2022 走看看