• 【详●析】[GXOI/GZOI2019]逼死强迫症


    【题目大意】

    (2 imes N)的方格中用(N-1)(2 imes 1)的方砖和(2)(1 imes 1)的方砖填充,且两块(1 imes 1)的方块不能有相邻的边,求合法方案数。

    【分析】

    啊,一道计数问题。反正我开始是这样想的。

    如果没有那两块很碍事的砖,这不就是斐波拉契递推吗?,,,(f[i]=f[i-1]+f[i-2]),递推走起。

    好,现在来看那两块碍事的砖。

    首先,我们会发现,这两块特别的砖会把整个方格分成三个部分,我们假设左右两部分刚好是完整的(即是个矩形),那么中间的块就有性质了。

    仔细推一推就会发现,当这两个特殊的块间隔奇数个块时,这两个块必定在相异的两行,并且中间只有一种方案构成。

    同样的,当这两个特殊的块间隔偶数个块时,这两个块必定在相同的一行,并且中间也只有一种方案构成。

    又因为不能有相邻的边,于是计算公式就出来了。

    [ans = 2 * sum_{i=0}^{n-3}sum_{j=0}^{i}f[j]*f[i-j] ]

    细细理解下。

    用这个大概只能得(20pt),我们想想怎么优化?看到(N<=2e+9)的数据范围,当然要往矩阵快速幂上面想咯。

    矩阵的推法各有不同吧,我们来一步一步来拆这个式子。

    (g(i)=sum_{j=0}^{i}f(j)*f(i-j))

    所以

    (egin{equation} egin{aligned} g(i)&=sum_{j=0}^{i}f(j)*f(i-j) \ &=sum_{j=0}^{i-2}f(j)*f(i-j)+f(i-1)*f(1)+f(i)*f(0)\ &=sum_{j=0}^{i-2}f(j)*[f(i-1-j)+f(i-2-j)]+f(i-1)+f(i)\ &=g(i-2)+g(i-1)+f(i) end{aligned} end{equation})

    又设(sum(i)=sum_{j=0}^{i}g(j))

    所以

    (egin{equation} egin{aligned} sum(i)&=sum_{j=0}^{i}g(j) \ &=sum_{j=0}^{i-1}g(j)+g(i) \ &=sum(i-1)+g(i-2)+g(i-1)+f(i) end{aligned} end{equation})

    所以易推得矩阵转移方程:

    (egin{equation}{ left[ egin{array}{ccc} f(i) \ f(i-1) \ g(i) \ g(i-1) \ sum(i) \ end{array} ight ]} imes { left[ egin{array}{ccc} 1 & 1 & 0 & 0 & 0\ 1 & 0 & 0 & 0 & 0 \ 1 & 1 & 1 & 1 & 0 \ 0 & 0 & 1 & 0 & 0 \ 1 & 1 & 1 & 1 & 1 \ end{array} ight ]}={ left[ egin{array}{ccc} f(i+1)\ f(i)\ g(i+1)\ g(i)\ sum(i+1) end{array} ight ]} end{equation})

    敲公式比敲字累多了。。。

    于是(O(125logn))可过。

    哦,这个推法有点玄学,需要处理下边界情况,具体情况见代码。

    【Code】

    #include<ctime>
    #include<queue>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int MAX = 100000 + 5;
    const int mod = 1e9 + 7;
    inline int read(){
    	int f = 1, x = 0;char ch;
    	do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0'||ch>'9');
    	do {x = x*10+ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    	return f*x;
    }
    
    struct sakura {
    	ll mar[5][5];
    }A;
    
    int t, n;
    
    inline sakura mul(sakura A, sakura B) {
    	sakura C;
    	memset(C.mar, 0, sizeof (C.mar));
    	for (int i = 0;i <= 4; ++i) {
    	for (int k = 0;k <= 4; ++k) {		
    		for (int j = 0;j <= 4; ++j) {
    				C.mar[i][j] = (C.mar[i][j] + (A.mar[i][k] * B.mar[k][j]) % mod ) % mod;
    			}
    		}
    	}
    	return C;
    }
    
    inline sakura mi(sakura A, int c) {
    	sakura B;
    	B.mar[0][0] = 1, B.mar[1][0] = 1, B.mar[2][0] = 2, B.mar[3][0] = 1, B.mar[4][0] = 3;
        for (;c;c >>= 1) {
    		if (c & 1) 	B = mul(A, B);
    		A = mul(A, A);
    	}
    	return B;
    }
    int main(){
    	t = read();
    	while (t--) {
    		n = read();
    		if (n < 3) {
    			printf("0
    ");
    			continue;
    		}
    		if (n == 3) {
    			printf("2
    ");
    			continue;
    		}
    		A.mar[0][0] = 1, A.mar[0][1] = 1, A.mar[0][2] = 0, A.mar[0][3] = 0, A.mar[0][4] = 0;
    		A.mar[1][0] = 1, A.mar[1][1] = 0, A.mar[1][2] = 0, A.mar[1][3] = 0, A.mar[1][4] = 0;
    		A.mar[2][0] = 1, A.mar[2][1] = 1, A.mar[2][2] = 1, A.mar[2][3] = 1, A.mar[2][4] = 0;
    		A.mar[3][0] = 0, A.mar[3][1] = 0, A.mar[3][2] = 1, A.mar[3][3] = 0, A.mar[3][4] = 0;
    		A.mar[4][0] = 1, A.mar[4][1] = 1, A.mar[4][2] = 1, A.mar[4][3] = 1, A.mar[4][4] = 1;
    		sakura ans = mi(A, n - 4);
    		ans.mar[4][0] <<= 1;
    		ans.mar[4][0] %= mod;
    		printf("%d
    ", ans.mar[4][0]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    在WINDOWS任务计划程序下执行PHP文件 PHP定时功能的实现
    使用Sublime Text 3进行Markdown 编辑+实时预览
    ni_set()函数的使用 以及 post_max_size,upload_max_filesize的修改方法
    CORS跨域的概念与TP5的解决方案
    tp5模型笔记---多对多
    微信小程序 GMT+0800 (中国标准时间) WXSS 文件编译错误
    ESP8266 LUA脚本语言开发: 外设篇-GPIO输入检测
    ESP8266 LUA脚本语言开发: 外设篇-GPIO输出高低电平
    ESP8266 LUA脚本语言开发: 准备工作-LUA文件加载与变量调用
    ESP8266 LUA脚本语言开发: 准备工作-LUA开发说明
  • 原文地址:https://www.cnblogs.com/silentEAG/p/10927635.html
Copyright © 2020-2023  润新知