Luogu P5303 [GXOI/GZOI2019]逼死强迫症

怎么还有这么SB的省选题啊

首先容易想到枚举右边的一个(1 imes 1)的小方块所在的列(i),然后暴枚左边小方块所在的列(j)在哪里,然后我们发现所有(jin [1,i-1))的列都有且仅有一种选择,具体和两者纵坐标的差值有关,画个图就知道了

然后每一对(i,j)的贡献是多少呢,很简单,显然在([i,j])之间的列只有唯一的放置方案,那么要乘上的系数就是两边(2 imes (i-1))(2 imes (n-j))大小的放法了

我们令(2 imes i)大小的放置方案为(f_i),考虑每次在右边放,要么是竖着从(f_{i-1})转移而来,要么是横着放两个从(f_{i-2})转移来,因此这就是一个斐波那契数列

然后考虑前面那个暴力的思路,我们预处理出(f_i)(f_i)的前缀和(s_i)即可拿到50pts(注意右边的有上下两种方法互相等价,因此要( imes 2)

50pts暴力代码

#include<cstdio>
#define RI register int
using namespace std;
const int N=1e5+5,mod=1e9+7;
int t,n,ans,fib[N],sum[N];
inline void init(void)
{
	sum[0]=fib[0]=fib[1]=1; for (RI i=sum[1]=2;i<N;++i)
	fib[i]=(fib[i-2]+fib[i-1])%mod,sum[i]=(sum[i-1]+fib[i])%mod;
}
int main()
{
	for (scanf("%d",&t),init();t;--t)
	{
		RI i; for (scanf("%d",&n),ans=0,i=3;i<=n;++i)
		(ans+=2LL*sum[i-3]*fib[n-i]%mod)%=mod; printf("%d
",ans);
	}
	return 0;
}

然后我们再用类似(f_i)的推导方式推导答案(F_i),考虑如何从之前的状态转移而来

很容易用一样的思路得到,如果之前(i-1)列没有放(1 imes 1)的小方块,那么(F_i)可以从(F_{i-1})(F_{i-2})转移来

如果放了呢,那么其实就是上面暴力的转移过程,因为我们此时在第(i)列的放法唯一,因此系数就是(1)

所以综合一下就有(F_i=F_{i-2}+F_{i-1}+2 imes s_{i-3}),然后用斐波那契数列前缀和的性质(s_i=f_{i+2}-1)即可推出(F_i=F_{i-2}+F_{i-1}+2 imes f_{i-1}-2)

直接矩阵快速幂维护转移的(5)个值(有一个是常数项),复杂度(O(T imes 5^3 imes log n))

100pts矩乘CODE

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=5,mod=1e9+7;
struct Matrix
{
    int n,m; long long mat[N][N];
    inline Matrix(CI N=0,CI M=0) { n=N; m=M; memset(mat,0,sizeof(mat)); }
    inline long long* operator [] (CI x) { return mat[x]; }
    friend inline Matrix operator * (Matrix A,Matrix B)
    {
        Matrix C(A.n,B.m); for (RI i=0;i<C.n;++i)
        for (RI j=0;j<C.m;++j) for (RI k=0;k<A.m;++k)
        (C[i][j]+=A[i][k]*B[k][j])%=mod; return C;
    }
    friend inline Matrix operator ^ (Matrix A,long long p)
    {
        Matrix T(A.n,A.m); for (RI i=0;i<A.n;++i) T[i][i]=1;
        for (;p;p>>=1LL,A=A*A) if (p&1) T=T*A; return T;
    }
}; int t,n;
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d",&n); if (n<=2) { puts("0"); continue; }
        Matrix S(5,1),D(5,5); S[2][0]=S[3][0]=1; S[4][0]=2;
        D[0][0]=D[0][1]=D[1][0]=D[2][2]=D[2][3]=D[3][2]=D[4][4]=1;
        D[0][2]=2; D[0][4]=mod-1; S=(D^(n-1))*S; printf("%d
",S[0][0]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/10961051.html