题意:给你一个NxM的格子,每个格子可以涂黑色或者白色,但是不允许任意2x2的格子同色,最多有多少方案。
分析:注意要判断一个pattern是不是nice的,只要检查相邻的两行的状态。由于m很小,可以将行的状态用2^m的掩码来表示,dp[k][i]表示k行,最后一行状态为i的pattern个数,如果状态i和状态j不冲突,那么dp[k+1][j]+=dp[k][i]。由于问题中n很大,这样一行一行转移的动态规划肯定是吃不消的。注意每次的转移其实都是一样的,记dp[k]为一个列向量,建立一个2m*2m的01矩阵A,矩阵元素对应表示两行状态是否冲突,则dp[k+1]=A*dp[k],dp[n]=A^n*dp[0],而A^n可以利用经典的快速乘法运算来计算。
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <string>
#include <math.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
const int INF=1000000000;
const int maxlength=9999;
class bigint
{
public:
int oper, length, a[maxlength];
bigint(int = 0);
~bigint();
void check();
void operator = (bigint m);
void operator = (int m);
void operator = (char *s);
bool operator < (bigint m);
bool operator <= (bigint m);
bool operator > (bigint m);
bool operator >= (bigint m);
bool operator == (bigint m);
bool operator != (bigint m);
bigint operator - ();
bigint operator + (bigint m);
void operator += (bigint m);
bigint operator - (bigint m);
void operator -= (bigint m);
bigint operator * (bigint m);
bigint operator * (int m);
void operator *= (bigint m);
void operator *= (int m);
bigint operator / (bigint m);
bigint operator / (int m);
void operator /= (bigint m);
void operator /= (int m);
bigint operator % (bigint m);
bigint operator % (int m);
void operator %= (bigint m);
void operator %= (int m);
};
bigint::bigint(int v)
{
(*this) = v;
this -> check();
}
bigint::~bigint()
{
}
void bigint::check()
{
for(; length > 0 && a[length] == 0; length--);
if(length == 0)
oper = 1;
}
void bigint::operator = (bigint m)
{
oper = m.oper;
length = m.length;
memcpy(a, m.a, maxlength * sizeof(int));
this -> check();
}
void bigint::operator = (int m)
{
oper = (m > 0)? 1: -1;
m = abs(m);
memset(a, 0, maxlength*sizeof(int));
for(length = 0; m > 0; m = m / 10000)
a[++length] = m % 10000;
this -> check();
}
void bigint::operator = (char *s)
{
int i, L;
(*this) = 0;
if(s[0] == '-' || s[0] == '+')
{
if(s[0] == '-')
oper = -1;
L = strlen(s);
for(i = 0; i < L; i++)
s[i] = s[i+1];
}
L = strlen(s);
length = (L + 3) / 4;
for(i = 0; i < L; i++)
a[(L - i + 3) / 4] = a[(L - i + 3) / 4] * 10 + (s[i] - 48);
this -> check();
}
bool bigint::operator < (bigint m)
{
if(oper != m.oper)
return oper < m.oper;
if(length != m.length)
return oper * length < m.length * oper;
for(int i = length; i >= 1; i--)
if(a[i]!=m.a[i])
return a[i]*oper<m.a[i]*oper;
return false;
}
bool bigint::operator <= (bigint m)
{
return !(m < (*this));
}
bool bigint::operator>(bigint m)
{
return m < (*this);
}
bool bigint::operator >= (bigint m)
{
return !((*this) < m);
}
bool bigint::operator == (bigint m)
{
return (!((*this) < m)) && (!(m < (*this)));
}
bool bigint::operator != (bigint m)
{
return ((*this) < m) || (m < (*this));
}
bigint bigint::operator - ()
{
bigint c = (*this);
c.oper = -c.oper;
c.check();
return c;
}
bigint abs(bigint m)
{
bigint c = m;
c.oper = abs(c.oper);
c.check();
return c;
}
bigint bigint::operator + (bigint m)
{
if(m.length == 0)
return (*this);
if(length == 0)
return m;
if(oper == m.oper)
{
bigint c;
c.oper = oper;
c.length = max(length, m.length) + 1;
for(int i = 1, temp = 0; i <= c.length; i++)
c.a[i] = (temp = (temp / 10000 + a[i] + m.a[i])) % 10000;
c.check();
return c;
}
return (*this) - (-m);
}
bigint bigint::operator - (bigint m)
{
if(m.length == 0)
return (*this);
if(length == 0)
return (-m);
if(oper == m.oper)
{
bigint c;
if(abs(*this) >= abs(m))
{
c.oper = oper;
c.length = length;
for (int i = 1, temp = 0; i <= length; i++)
c.a[i] = ((temp = (-int(temp < 0) + a[i] - m.a[i])) + 10000) % 10000;
c.check();
return c;
}
return -(m - (*this));
}
return (*this) + (-m);
}
bigint bigint::operator * (bigint m)
{
bigint c;
c.oper = oper * m.oper;
c.length = length + m.length;
for(int i = 1; i <= m.length; i++)
{
int number = m.a[i],j,temp = 0;
for (j = 1; j <= length; j++)
c.a[i + j - 1] += number * a[j];
if (i % 10 == 0 || i == m.length)
for (j = 1; j <= c.length; j++)
c.a[j] = (temp = (temp / 10000) + c.a[j]) % 10000;
}
c.check();
return c;
}
bigint bigint::operator * (int m)
{
if(m < 0)
return -((*this) * (-m));
if(m > 100000)
return (*this) * bigint(m);
bigint c;
c.length = length + 2;
c.oper = oper;
int t = 0;
for(int i = 1; i <= c.length; i++)
c.a[i] = (t = (t / 10000 + a[i] * m)) % 10000;
c.check();
return c;
}
bigint bigint::operator / (bigint m)
{
if(m.length == 0)
{
printf("Division by zero.\n");
exit(0);
}
if(abs(*this) < abs(m))
return 0;
bigint c, left;
c.oper = oper / m.oper;
m.oper = 1;
c.length = length - m.length + 1;
left.length = m.length - 1;
memcpy(left.a + 1, a + length - left.length + 1, left.length * sizeof(int));
for(int i = c.length; i >= 1; i--)
{
left = left * 10000 + a[i];
int head = 0, tail = 10000, mid;
while(head + 1 < tail)
{
mid = (head + tail) >> 1;
if(m * mid <= left)
head = mid;
else
tail = mid;
}
c.a[i] = head;
left -= m * head;
}
c.check();
return c;
}
bigint bigint::operator / (int m)
{
if(m < 0)
return -((*this) / (-m));
if(m > 100000)
return (*this) / bigint(m);
bigint c;
c.oper = oper;
c.length = length;
int t = 0;
for(int i = c.length; i >= 1; i--)
c.a[i] = (t = (t % m * 10000 + a[i])) / m;
c.check();
return c;
}
bigint bigint::operator % (bigint m)
{
return (*this) - ((*this) / m) * m;
}
bigint bigint::operator % (int m)
{
if (m < 0)
return -((*this) % (-m));
if (m > 100000)
return (*this) % bigint(m);
int t = 0;
for(int i = length; i >= 1; i--)
t = (t * 10000 + a[i]) % m;
return t;
}
void bigint::operator += (bigint m)
{
(*this) = (*this) + m;
}
void bigint::operator -= (bigint m)
{
(*this) = (*this) - m;
}
void bigint::operator *= (bigint m)
{
(*this) = (*this) * m;
}
void bigint::operator /= (bigint m)
{
(*this) = (*this) / m;
}
void bigint::operator %= (bigint m)
{
(*this) = (*this) % m;
}
void bigint::operator *= (int m)
{
(*this) = (*this) * m;
}
void bigint::operator /= (int m)
{
(*this) = (*this) / m;
}
void bigint::operator %= (int m)
{
(*this) = (*this) % m;
}
int mod;
const int maxn=40;
struct p
{
int m[maxn][maxn];
int h,l;
p()
{
memset(m,0,sizeof(m));
}
};
p multi(p a,p b)
{
int i,j,k;
p c;
for(i=1;i<=a.h;i++)
{
for(j=1;j<=b.l;j++)
{
c.m[i][j]=0;
for(k=1;k<=a.h;k++)
c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
}
}
c.h=a.h; c.l=b.l;
return c;
}
p quickpow(p a,bigint n)
{
int i;
p s;
s.h=s.l=a.h;
for(i=1;i<=a.h;i++)
s.m[i][i]=1;
while(n!=0)
{
if(n%2!=0)
s=multi(s,a);
a=multi(a,a);
n=n/2;
}
return s;
}
void qs(int* a,int ws,int shu)
{
int ans=0,i;
for(int i=1;i<=ws;i++)a[i]=0;
while(shu)
{
a[++ans]=shu%2;
shu=shu/2;
}
for(i=1;i<=ws/2;i++)swap(a[i],a[ws-i+1]);
//for(i=1;i<=ws;i++)printf("%d",a[i]);
//printf("\n");
}
int pd(int* a,int* b,int ws)
{
for(int i=1;i<ws;i++)if(a[i]==a[i+1]&&b[i]==b[i+1]&&a[i]==b[i])return 0;
return 1;
}
int main()
{
int t,m,i,j,mp[35][35],a[7],b[7],ret,cs=0;
char s[10000];
bigint B;
scanf("%d",&t);
while(t--)
{
if(cs>0)printf("\n");
cs++;
scanf("%s%d%d",s,&m,&mod);
B=s;
for(i=0;i<(1<<m);i++){
qs(a,m,i);
for(j=i;j<(1<<m);j++){
qs(b,m,j);
if(pd(a,b,m))mp[i+1][j+1]=1;
else mp[i+1][j+1]=0;
}
}
for(i=1;i<=(1<<m);i++)
for(j=1;j<=i;j++)mp[i][j]=mp[j][i];
//for(i=1;i<=(1<<m);i++){
//for(j=1;j<=(1<<m);j++)printf("%d",mp[i][j]);
//printf("\n");
// }
p c;
for(i=1;i<=(1<<m);i++)
for(j=1;j<=(1<<m);j++)c.m[i][j]=mp[i][j];
c.h=c.l=(1<<m);
c=quickpow(c,B-1); ret=0;
for(i=1;i<=(1<<m);i++){
for(j=1;j<=(1<<m);j++)ret+=c.m[i][j];
}
printf("%d\n",ret%mod);
}
return 0;
}