[牛客OI周赛3-提高组A] 地斗主 [dp][矩阵快速幂]

link

有道类似的棋盘覆盖(不过棋盘不是 4 × m \frak{4×m} )叫做 M o n d r i a a n s D r e a m \frak{Mondriaan's Dream}
弱化了范围的原题也有:
数据范围弱化( p o j \frak{poj} )
数据范围弱化( h d u \frak{hdu} )

另外我觉得应该有原题(
u p d . \frak{upd.} 原题找到了, l i n k \frak{link}

这一类棋盘覆盖问题可以先推出来一个 d p \frak{dp} 然后用矩阵搞转移
值得一提的是还可以用组合学公式来做
据说棋盘覆盖问题在生物学上叫做二聚物问题,好像还有推导公式?(

转回这道题。可以考虑用“上一行覆盖到下一行的情况”来转移。
然后可以列出一个 d f a \frak{dfa} 图,实际上是一个矩阵,用快速幂转移。

不会传数组引用直接把矩乘暴力贴里面了 程序可能有点丑(

官方题解:
随便手摸一下,我们可以得到…我们发现…我们可以把公式变为…………然后就推出矩阵了
我:???

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cctype>
#include<ctime>
#include<cstdlib>
using namespace std;
int T;
long long N,M,tmp[5][5]={};
long long transfer[5][5]={{1,1,1,1,0},{1,0,0,0,0},{2,0,1,0,0},{1,0,0,0,1},{0,0,0,1,0}};
long long qmpow(long long t)
{
	long long ans[5][5]={{1,0,0,0,0},{0,1,0,0,0},{0,0,1,0,0},{0,0,0,1,0},{0,0,0,0,1}};
	long long base[5][5],ret=0;
    for(int i=0;i<5;++i)
    {
    	for(int j=0;j<5;++j)
    	{
    		base[i][j]=transfer[i][j];
    	}
    }
    while(t)
	{
        if(t&1)
		{
			for(int i=0;i<5;++i)
			{
				for(int j=0;j<5;++j)
				{
					tmp[i][j]=0;
				}
			}
   			for(int i=0;i<5;++i)
			{
        		for(int j=0;j<5;++j)
				{
            		for(int k=0;k<5;++k)
					{
               			tmp[i][j]=(tmp[i][j]+ans[i][k]*base[k][j]%M)%M;
            		}
        		}
    		}
    		for(int i=0;i<5;++i)
    		{
    			for(int j=0;j<5;++j)
    			{
    				ans[i][j]=tmp[i][j];
    			}
    		}
        }
		for(int i=0;i<5;++i)
		{
			for(int j=0;j<5;++j)
			{
				tmp[i][j]=0;
			}
		}
  		for(int i=0;i<5;++i)
		{
       		for(int j=0;j<5;++j)
			{
        		for(int k=0;k<5;++k)
				{
           			tmp[i][j]=(tmp[i][j]+base[i][k]*base[k][j]%M)%M;
        		}
    		}
		}
		for(int i=0;i<5;++i)
		{
			for(int j=0;j<5;++j)
			{
				base[i][j]=tmp[i][j];
			}
 		}
        t>>=1;
    }
    ret=(ret+5ll*ans[0][0]%M)%M;
    ret=(ret+1ll*ans[1][0]%M)%M;
    ret=(ret+2ll*ans[2][0]%M)%M;
    ret=(ret+1ll*ans[3][0]%M)%M;
    ret=(ret+1ll*ans[4][0]%M)%M;
    return ret;
}
int main()
{
	scanf("%d",&T);
    while(T--)
	{
		scanf("%lld%lld",&N,&M);
        if(N==1ll)
		{
            printf("%lld\n",1ll%M);
            continue;
        }
        if(N==2ll)
		{
            printf("%lld\n",5ll%M);
            continue;
        }
        printf("%lld\n",qmpow(N-2ll));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/83244687