题目要求:
用1*2的骨牌完全覆盖n*m棋盘,问有多少种方案
1<=n<=5,1<=m<=10^18
答案998244353取模
思路
你可以当我虚线下面是废话,我想了两天终于想出来了,我是菜鸡
总的来说就是矩阵快速幂加状态压缩dp
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5,M=1<<N,mod=998244353;
int n,m;
bool st[M];
int A[M][M],tmp[M][M],res[M][M];
void MXMP(int a[][M], int b[][M])
{
memset(tmp, 0, sizeof tmp);//tmp用来存放相乘的结果
for (int i = 0; i < (1<<n); i++) {
for (int j = 0; j < (1<<n); j++) {
for (int k = 0; k < (1<<n); k++) {
tmp[i][j] = (tmp[i][j] + (a[i][k] * b[k][j]) % mod) % mod;//记得摸mod
}
}
}
for (int i = 0; i < (1<<n); i++) {
for (int j = 0; j < (1<<n); j++) {
a[i][j] = tmp[i][j];//记得把tmp赋值给a
}
}
}
void powermod(int A[][M], int x)
{
memset(res, 0, sizeof res);//res存放结果
for (int i = 0; i < (1<<n); i++) {
for (int j = 0; j < (1<<n); j++) {
res[i][j] = A[i][j];
}
}
while (x) {
if (x & 1) MXMP(res, A);//x为奇数就将A与res相乘
MXMP(A, A);//A自身相乘
x >>= 1;//x除以二
}
for (int i = 0; i < (1<<n); i++) {//将结果赋值到A
for (int j = 0; j < (1<<n); j++) {
A[i][j] = res[i][j];
}
}
}
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0),cout.tie(0);
cin>>n>>m;
if((n&1)&&(m&1)){//判断是否都是奇数
cout<<0;
return 0;
}
memset(st,true,sizeof st);//记录是否可以放
for(int i=0;i<(1<<n);i++){
int cnt=0;
for(int j=0;j<n;j++){
if((i>>j)&1){
if(cnt&1){
st[i]=false;
break;
}
}else cnt++;
}
if(cnt&1) st[i]=false;
}
memset(A,0,sizeof A);
for(int j=0;j<(1<<n);j++){
for(int k=0;k<(1<<n);k++){
if((j&k)==0&&st[j|k]){
A[j][k]=1; //记录是由哪个状态可以继承过来
}
}
}
m-=1;//因为res是由A赋值过去的所以res*A会多算一次得减一
powermod(A,m);//矩阵快速幂
cout<<A[0][0];
/*for(int i=0;i<(1<<n);i++){
for(int j=0;j<(1<<n);j++){
cout<<A[i][j]<<' ';
}
cout<<'\n';
}*/
}
---------------------------------------------------------------------------------------------------------------------------------
原本没看范围以为就是一道简单的状态压缩dp很开心的按版子打上去交了一发就超时了
后面一看范围10的18次方,比赛时也是没有做出来
这是状态压缩dp的代码(超时)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5,M=1<<N;
int n,m;
bool st[M];
int f[M][M];
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0),cout.tie(0);
cin>>n>>m;
memset(st,true,sizeof st);
for(int i=0;i<(1<<n);i++){
int cnt=0;
for(int j=0;j<n;j++){
if((i>>j)&1){
if(cnt&1){
st[i]=false;
break;
}
}else cnt++;
}
if(cnt&1) st[i]=false;
}
memset(f,0,sizeof f);
f[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<(1<<n);j++){
for(int k=0;k<(1<<n);k++){
if((j&k)==0&&st[j|k]){
f[i][j]+=f[i-1][k];
}
}
}
}
cout<<f[m][0];
}
后面我们还是找了一下规律当n=1 就是0,1,0,1,0,1
n=2就是1,2,3,5,8,13
发现是一个斐波那契数列,这时候m可以取到10^18正常求肯定会超时,但是斐波那契还可以用矩阵快速幂来求,这时候我就有大致思路,是不是整体都可以用矩阵快速幂来优化
下面是求斐波那契的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
int A[2][2] = { {1,1},{1,0} };
int res[2][2], n;
int tmp[2][2];
void MXMP(int a[][2], int b[][2])
{
memset(tmp, 0, sizeof tmp);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
tmp[i][j] = (tmp[i][j]%mod + (a[i][k] * b[k][j]) % mod) % mod;
}
}
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
a[i][j] = tmp[i][j]%mod;
}
}
}
void powermod(int A[][2], int x)
{
memset(res, 0, sizeof res);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
res[i][j] = 1;
}
}
while (x) {
if (x & 1) MXMP(res, A);
MXMP(A, A);
x >>= 1;
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
A[i][j] = res[i][j];
}
}
}
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0), cout.tie(0);
cin >> n;
n-=1;
if(n==0||n==1){
cout<<1;
return 0;
}
powermod(A, n - 2);
int q = (A[0][1] + A[0][0])%mod;
cout << q;
}
然后我n=3是1,0,3,0,11,0
我想这先找一下偶数的规律
然后去找了n=4 1,1,5,11,36,95,281
看他数出现的规律然后去推断是由哪个数继承过来的
我把出现过数字的号码记下来了
看上图1可以加到12456进去,2可以加到12进去
后面想到可以用矩阵就是右边那图
可以写成n==4的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
int A[6][6] = { {1,1,0,1,1,1},{1,1,0,0,0,0},{0,0,0,1,0,0},{1,0,1,0,0,0},{1,0,0,0,1,0},{1,0,0,0,0,0}};
int res[6][6], n;
int tmp[6][6];
void MXMP(int a[][6], int b[][6])
{
memset(tmp, 0, sizeof tmp);
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
for (int k = 0; k < 6; k++) {
tmp[i][j] = (tmp[i][j] + (a[i][k] * b[k][j]) % mod) % mod;
}
}
}
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
a[i][j] = tmp[i][j];
}
}
}
void powermod(int A[][6], int x)
{
memset(res, 0, sizeof res);
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
res[i][j] = A[i][j];
}
}
while (x) {
if (x & 1) MXMP(res, A);
MXMP(A, A);
x >>= 1;
}
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
A[i][j] = res[i][j];
}
}
}
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0), cout.tie(0);
cin >> n;
n-=2;
if(n==-1){
cout<<1;
return 0;
}
powermod(A, n);
int q = A[0][0];
cout << q;
}
做到这里突然就悟了,就是打一张表看能不能继承在用一下快速幂就行了
卧槽我太聪明了
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5,M=1<<N,mod=998244353;
int n,m;
bool st[M];
int A[M][M],tmp[M][M],res[M][M];
void MXMP(int a[][M], int b[][M])
{
memset(tmp, 0, sizeof tmp);//tmp用来存放相乘的结果
for (int i = 0; i < (1<<n); i++) {
for (int j = 0; j < (1<<n); j++) {
for (int k = 0; k < (1<<n); k++) {
tmp[i][j] = (tmp[i][j] + (a[i][k] * b[k][j]) % mod) % mod;//记得摸mod
}
}
}
for (int i = 0; i < (1<<n); i++) {
for (int j = 0; j < (1<<n); j++) {
a[i][j] = tmp[i][j];//记得把tmp赋值给a
}
}
}
void powermod(int A[][M], int x)
{
memset(res, 0, sizeof res);//res存放结果
for (int i = 0; i < (1<<n); i++) {
for (int j = 0; j < (1<<n); j++) {
res[i][j] = A[i][j];
}
}
while (x) {
if (x & 1) MXMP(res, A);//x为奇数就将A与res相乘
MXMP(A, A);//A自身相乘
x >>= 1;//x除以二
}
for (int i = 0; i < (1<<n); i++) {//将结果赋值到A
for (int j = 0; j < (1<<n); j++) {
A[i][j] = res[i][j];
}
}
}
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0),cout.tie(0);
cin>>n>>m;
if((n&1)&&(m&1)){//判断是否都是奇数
cout<<0;
return 0;
}
memset(st,true,sizeof st);//记录是否可以放的了
for(int i=0;i<(1<<n);i++){
int cnt=0;
for(int j=0;j<n;j++){
if((i>>j)&1){
if(cnt&1){
st[i]=false;
break;
}
}else cnt++;
}
if(cnt&1) st[i]=false;
}
memset(A,0,sizeof A);
for(int j=0;j<(1<<n);j++){
for(int k=0;k<(1<<n);k++){
if((j&k)==0&&st[j|k]){
A[j][k]=1; //记录是由哪个状态可以继承过来
}
}
}
m-=1;//因为res是由A赋值过去的所以res*A会多算一次得减一
powermod(A,m);//矩阵快速幂
cout<<A[0][0];
/*for(int i=0;i<(1<<n);i++){
for(int j=0;j<(1<<n);j++){
cout<<A[i][j]<<' ';
}
cout<<'\n';
}*/
}