您的位置:首页 > 其它

【NOIP模拟】数格子

2016-09-19 15:00 176 查看

Description



Solution

一道很经典的题目的简单版。

很显然是状压DP。

首先4*n可以变成n*4,然后状态数就只有16个了。(1表示有凸起,0表示没有凸起)

设f[i][j]表示第i层的状态数为j的时候的方案数。

一开始可以先预处理一个a[i][j]表示状态i是否能转移到状态j。

如果能转移到,那么要具备两个条件:

1、i&j==0,这个很明显,如果两个都填1,那么就说明会对下一层造成影响。

2、pan(i+j)==1,pan(i)表示i转态里面是否有连续的次数为奇数的1,如果有就是不合法值为0,否则值为1。为什么上下两层转态加起来一定要没有连续的奇数个1。首先,对于第一行来说这是必须的,然后对于后面的排来说如果上面有0,那么下面可以填1,如果有不填的情况就是上面有两个0,刚好可以横着放,导致下一层也有两个零。那么就是说两层放竖板的地方加起来不会有连续为奇数的情况。

那么之后DP完,再打个矩阵乘法就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100007;
int i,j,k,l,t,n,m,ans;
int er[10];
typedef long long ll;
struct ju{
ll a[16][16];
ju friend operator *(ju a,ju b){
ju c;memset(c.a,0,sizeof(c.a));
int i,j,k;
fo(i,0,15){
fo(j,0,15){
fo(k,0,15){
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
}
}
}
return c;
}
}a,b;
bool pan(int x){
int i,j=0;
fo(i,1,4){
if((x&er[i])==0){
if(j%2)return 0;
j=0;
}
else j++;
}
if(j%2)return 0;
return 1;
}
bool guhou(int x,int y){
if(((x&y)==0)&&pan(x+y))return 1;return 0;
}
ju qsm(ju x,int y){
ju z;
memset(z.a,0,sizeof(z.a));fo(i,0,15)z.a[i][i]=1;
while(y){
if(y&1)z=z*x;
x=x*x;
y/=2;
}
return z;
}
int main(){
fo(i,1,9)er[i]=1<<(i-1);
fo(i,0,15){
fo(j,0,15){
if(guhou(i,j)){
a.a[i][j]=1;
}
}
}
while(1){
scanf("%d%d",&n,&m);
if(!n&&!m)break;
memset(b.a,0,sizeof(b.a));
fo(i,0,15)if(pan(i))b.a[0][i]=1;
b=qsm(a,n)*b;
printf("%lld\n",b.a[0][0]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: