Codeforces Gym 100199E

题目链接:http://acm.hust.edu.cn/vjudge/problem/109646

题目大意:

给出一个m*n的矩阵,要求每格用黑色或白色填充,且任意一个2x2的部分不能同色,求有多少种填充方案。 1<=n<=10^100,1<=m<=5

分析:

注意到m只有5,而相比之下n的范围却大的惊人,因此我们可以枚举每一列的状态(最多32种),枚举得出哪两列可以相邻,建出一个初始图。

不难得出DP转移方程:设F[i][s]为在第i列为状态S的情况下有多少种填法,则F[i][s]=sum(F[i-1][K]) (K为可以与S相邻的状态)

但这样做时间是O(n)的,难以承受。

因此我们可以用递推数列中常用的优化方法:矩阵快速幂

这里写图片描述

如图所示,将F序列变换到G序列,需要乘上一个系数矩阵。

如果需要多次变换,那么我们只要将矩阵自乘就可以了。

而矩阵的幂是O(m^3logn)的,在这里因为矩阵很小,m可忽略。于是我们就将递推做到了对数级别的时间。

这种方法适用面很广,基本不涉及最优决策的DP都可以用这种方式优化,比如求斐波拉契数列的第n项。而且矩阵也比较好构造,代码复杂度也不高。

关于n高精度问题,可以用十进制快速幂解决(感谢cx大爷提供方法)

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define bit(x,d) (((x&(1<<d))==0)?0:1)

using namespace std;

const int maxn=100+23;

int ans,m,p,len,Mba[maxn][maxn],M[maxn][maxn];

char n[maxn];

void init() //初始构造出基本矩阵
{
 memset(Mba,0,sizeof(Mba));

 rep(i,0,(1<<m)-1)
  rep(j,0,(1<<m)-1)
  {
   bool mark=1;
   rep(k,1,m-1)
    if ((bit(i,k)==bit(i,k-1))&&(bit(j,k)==bit(j,k-1))&&(bit(i,k)==bit(j,k))) {mark=0;break;}
   if (mark) Mba[i][j]=1; 
  } 
}

void mul(int M[maxn][maxn],int a[maxn][maxn],int b[maxn][maxn]) //矩阵乘法
{
 int temp[maxn][maxn];

 rep(i,0,(1<<m)-1)
  rep(j,0,(1<<m)-1)
  {
   temp[i][j]=0;
   rep(k,0,(1<<m)-1) temp[i][j]+=a[i][k]*b[k][j];
   temp[i][j]%=p;
  }

 rep(i,0,(1<<m)-1) rep(j,0,(1<<m)-1) M[i][j]=temp[i][j]; 
}

void cmp(int a[maxn][maxn],int M[maxn][maxn])
{
 rep(i,0,(1<<m)-1) rep(j,0,(1<<m)-1) M[i][j]=a[i][j];
}

void get_pow(int M[maxn][maxn],int k) //快速幂
{
 if (k==0) 
 {
  memset(M,0,sizeof(M));
  rep(i,0,(1<<m)-1) M[i][i]=1;
 }
 else 
 {
  int p[maxn][maxn];get_pow(p,k-1),cmp(p,M);

  rep(i,1,9) mul(M,M,p);

  rep(i,1,n[k]-'0') mul(M,Mba,M);
 }
}

int main()
{
 freopen("nice.in","r",stdin);
 freopen("nice.out","w",stdout);
 
 scanf("%s%d%d",n+1,&m,&p);
 
 len=strlen(n+1);

 n[len]--;
 
 dep(i,len,1) 
  if (n[i]<'0')
  {
   n[i-1]--;
   n[i]+=10;
  } 

 if (n[1]=='0') {rep(i,1,len) n[i]=n[i+1];len--;}

 init();

 get_pow(M,len);
 
 ans=0;

 rep(i,0,(1<<m)-1) rep(j,0,(1<<m)-1) ans+=M[i][j];

 printf("%d\n",ans%p);

 return 0;
}
posted @ 2016-07-22 10:00  Krew  阅读(268)  评论(0)    收藏  举报