zoukankan      html  css  js  c++  java
  • 【洛谷5303】[GXOI/GZOI2019] 逼死强迫症(矩乘)

    点此看题面

    • 要用(n)(2 imes 1)的方砖去铺一条(2 imes n)的路。
    • 其中有一块方砖裂成了两块(1 imes 1)的方砖,问有多少种铺路的方案使得裂成的两块方砖没有相邻边。
    • 数据组数(le500,nle2 imes10^9)

    (1 imes1)方砖两侧的填法

    假设铺一条(2 imes i)的路的方案数为(f_i)

    考虑对于第一列第一个格子,放在它上面的方砖只有两种方式——竖着或是横着。

    • 如果竖着,那么接下来相当于要铺一条(2 imes(i-1))的路,所以(f_i)可以由(f_{i-1})转移。
    • 如果横着,考虑这样一来第一列第二个格子也必须横着,相当于把前两列都填完了,因此接下来要铺(2 imes(i-2))的路,所以(f_i)可以由(f_{i-2})转移。

    综上所述,(f_i=f_{i-1}+f_{i-2}),也就是说它是斐波那契数!

    加上(1 imes 1)方砖后的填法

    考虑我们要铺一条(2 imes i)的路,强制(1 imes 1)的方砖分别在路的两侧。

    如果(ile2),显然无解。

    否则,发现当(i)为奇数时,两个方砖只能一个左上一个右下,或是一个左下一个右上,且中间的方砖只有唯一的填法;而当(i)为偶数时,两个方砖只能同时在上或同时在下,中间的方砖也只有唯一的填法。

    所以说我们设加上(1 imes1)的方砖后铺一条(2 imes i)的路的方案数为(F_i)

    依旧考虑第一列的两个格子,那么首先第一种情况就是它可以不放(1 imes 1)的方砖,那么转移和之前类似,就是从(F_{i-1}+F_{i-2})转移。

    还有一种情况就是它放了(1 imes 1)的方砖,它有两种摆放方式,因此可以从(2sum_{j=0}^{i-3}f_j)转移(注意是(f),因为只能填一次(1 imes1)的方砖)。

    (sum_{j=0}^{i-3}f_j)可以直接维护,当然更好的方式是看出这就等于(f_{i-1}-1)(证明考虑归纳,首先(f_0=f_2-1),那么每次给(sum_{j=0}^{i-3}f_j=f_{i-1}-1)两侧同时加上(f_{i-2})即可得到(sum_{j=0}^{i-2}f_j=f_{i-2}+f_{i-1}-1=f_i-1))。

    (n)这么大显然用矩阵加速,只要维护(F_i,F_{i-1},f_i,f_{i-1},2)五个值即可。

    代码:(O(Tlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define X 1000000007
    using namespace std;
    int n;struct M
    {
    	int a[5][5];I M() {memset(a,0,sizeof(a));}
    	I int* operator [] (CI x) {return a[x];}I Con int* operator [] (CI x) Con {return a[x];}
    	I M operator * (Con M& o) Con {M t;RI i,j,k;for(i=0;i^5;++i)//矩阵乘法
    		for(j=0;j^5;++j) for(k=0;k^5;++k) t[i][j]=(1LL*a[i][k]*o[k][j]+t[i][j])%X;return t;}
    	I M operator ^ (RI y) Con {M x=*this,t;for(RI i=0;i^5;++i) t[i][i]=1;W(y) y&1&&(t=t*x,0),x=x*x,y>>=1;return t;}//矩阵快速幂
    }S,U,G;
    int main()
    {
    	S[2][2]=S[3][3]=1,S[4][4]=2,U[0][0]=U[0][1]=U[1][0]=U[2][2]=U[2][3]=U[3][2]=U[4][4]=1,U[2][0]=2,U[4][0]=X-1;//初始化初始矩阵和转移矩阵
    	RI Tt;scanf("%d",&Tt);W(Tt--) scanf("%d",&n),G=S*(U^(n-1)),printf("%d
    ",(0LL+G[2][0]+G[3][0]+G[4][0])%X);return 0;//每次直接矩阵快速幂
    }
    
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    面向对象的三大特性(封装、继承、多态)-----继承
    前端---HTML
    几个排序算法的python实现
    构造方法关键字---this
    构造方法中关键字-- super
    多态(instanceof)
    接口
    抽象
    继承(重写与重载对比)
    数组的逆序
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5303.html
Copyright © 2011-2022 走看看