题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2317
题意:给一个N*M的矩阵,填黑、白两种颜色,问其中不能有2*2的矩阵是一样颜色的方案数?方案数太大,输出%p之后的结果。
思路:N<10^100,M<=5,一看数据范围就能猜到是在M上研究。因为M非常小,所以我们可以枚举每一列的状态,总共只有2^5=32中状态,这样我们就可以处理出来一个32*32的矩阵,a.m[ i ][ j ]=1表示 i 代表的状态可以和 j 代表的状态相邻,这样只需要求出该矩阵的(N-1)次幂,然后累加矩阵每一个位置的值就是答案。因为N很大,这里做快速幂的时候还要用到高精度!当然也可以用java写更方便一些,懒得下eclipse,就手写高精度了。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
int m,p,tm;
string n;
struct node
{
LL m[33][33];
}a,res;
const int L=150;
string subb(string a,string b)
{
string ans;
int na[L]={0},nb[L]={0};
int la=a.size(),lb=b.size();
for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
int lmax=la>lb?la:lb;
for(int i=0;i<lmax;i++)
{
na[i]-=nb[i];
if(na[i]<0) na[i]+=10,na[i+1]--;
}
while(!na[--lmax]&&lmax>0) ;lmax++;
for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
return ans;
}
int sub(int *a,int *b,int La,int Lb)
{
if(La<Lb) return -1;
if(La==Lb)
{
for(int i=La-1;i>=0;i--)
if(a[i]>b[i]) break;
else if(a[i]<b[i]) return -1;
}
for(int i=0;i<La;i++)
{
a[i]-=b[i];
if(a[i]<0) a[i]+=10,a[i+1]--;
}
for(int i=La-1;i>=0;i--)
if(a[i]) return i+1;
return 0;
}
string div(string n1,string n2,int nn)
{
string s,v;
int a[L],b[L],r[L],La=n1.size(),Lb=n2.size(),i,tp=La;
fill(a,a+L,0);fill(b,b+L,0);fill(r,r+L,0);
for(i=La-1;i>=0;i--) a[La-1-i]=n1[i]-'0';
for(i=Lb-1;i>=0;i--) b[Lb-1-i]=n2[i]-'0';
if(La<Lb || (La==Lb && n1<n2))
{
return "0";
}
int t=La-Lb;
for(int i=La-1;i>=0;i--)
if(i>=t) b[i]=b[i-t];
else b[i]=0;
Lb=La;
for(int j=0;j<=t;j++)
{
int temp;
while((temp=sub(a,b+j,La,Lb-j))>=0)
{
La=temp;
r[t-j]++;
}
}
for(i=0;i<L-10;i++) r[i+1]+=r[i]/10,r[i]%=10;
while(!r[i]) i--;
while(i>=0) s+=r[i--]+'0';
i=tp;
while(!a[i]) i--;
while(i>=0) v+=a[i--]+'0';
if(v.empty()) v="0";
if(nn==1) return s;
if(nn==2) return v;
}
void init()
{
int x[10],y[10];
for(int i=0;i<tm;i++)
for(int j=0;j<tm;j++)
{
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
int k = i;
while(k)
{
if(k % 2 == 0)
x[++x[0]] = 0;
else
x[++x[0]] = 1;
k >>= 1;
}
k = j;
while(k)
{
if(k % 2 == 0)
y[++y[0]] = 0;
else
y[++y[0]] = 1;
k >>= 1;
}
int flag = 0,sum;
for(int k=1;k<m;k++)
{
sum = x[k] + x[k+1] + y[k] + y[k+1];
if(sum == 0 || sum == 4)
{
flag = 1;
break;
}
}
if(flag)
a.m[i][j] = 0;
else
a.m[i][j] = 1;
}
}
node mul(node a,node b)
{
node c;
for(int i=0;i<tm;i++)
for(int j=0;j<tm;j++)
{
c.m[i][j] = 0;
for(int k=0;k<tm;k++)
c.m[i][j] = (c.m[i][j] + a.m[i][k]*b.m[k][j]) % p;
}
return c;
}
int check(string k)
{
int u = k[k.size()-1] - '0';
if(u % 2 == 1)
return 1;
return 0;
}
void ksm(string k)
{
memset(res.m,0,sizeof(res.m));
for(int i=0;i<tm;i++) res.m[i][i] = 1;
while(k != "0")
{
int v = check(k);
if(v)
res = mul(res,a);
a = mul(a,a);
k = div(k,"2",1);
}
}
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while(T--)
{
cin >> n >> m >> p;
n = subb(n,"1");
memset(a.m,0,sizeof(a.m));
tm = 1;
for(int i=1;i<=m;i++) tm *= 2;
init();
ksm(n);
int ans = 0;
for(int i=0;i<tm;i++)
for(int j=0;j<tm;j++)
ans = (ans+res.m[i][j]) % p;
cout << ans << endl;
if(T != 0) cout << endl;
}
return 0;
}