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

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

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

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

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

然后考虑前面那个暴力的思路,我们预处理出\(f_i\)\(f_i\)的前缀和\(s_i\)即可拿到50pts(注意右边的有上下两种方法互相等价,因此要\(\times 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\n",ans);
	}
	return 0;
}

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

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

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

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

直接矩阵快速幂维护转移的\(5\)个值(有一个是常数项),复杂度\(O(T\times 5^3\times \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\n",S[0][0]);
    }
    return 0;
}
posted @ 2019-06-01 21:22  空気力学の詩  阅读(185)  评论(0编辑  收藏  举报