Gym - 101635C Macarons
Step1 Problem:
给你 n*m 的矩阵,你可以填 1 * 1 的黑块,或者 1 * 2 的黑块,或者 2 * 1 的黑块。求有多少填的方案可以将矩阵填满。
数据范围:
1 <= n <= 8, 1 <= m <= 1e18.
Step2 Ideas:
感谢 dq 给我讲解了一波,发现了一篇很棒的博客。
前置技能:你需要将经典题目 8 先学会了。
这是你就知道你需要构造一个状态转移矩阵。
我们可以枚举第 i 列所有黑块所能产生的样子,然后将第 i 列填满所构成的第 i+1 列的状态,就是所枚举的第 i 列状态能到达第 i+1 列状态。
核心注意点:所填黑块必须和第 i+1 列有关,不然该状态就不是我们所枚举想要的状态了。博客里面有讲。
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1<<8;
const int MOD = 1e9;
struct node
{
ll a[N][N];
};
node c;
int n;
void dfs(int u, int now, int nex)
{
if(u == n) {
c.a[now][nex]++;//now 状态 能到达 nex 状态
return ;
}
if(now>>u&1) {//当前位置有黑块
dfs(u+1, now, nex);//不填
dfs(u+1, now, nex|(1<<u));//填 1 * 1
if(u+2<=n && (now&(1<<(u+1)))) dfs(u+2, now, (nex|(1<<u))|(1<<(u+1))); // 下一个位置也有黑块,可以直接对第 i+1 列填 2*1.
}
else {
dfs(u+1, now, nex|(1<<u));//当前位置为空得填满,填 1*2.
}
}
node mul(node x, node y)
{
node ans;
memset(ans.a, 0, sizeof(ans.a));
int m = (1<<n);
for(int k = 0; k < m; k++)
{
for(int i = 0; i < m; i++)
{
if(x.a[i][k])
for(int j = 0; j < m; j++)
{
ans.a[i][j] += x.a[i][k]*y.a[k][j];
ans.a[i][j] %= MOD;
}
}
}
return ans;
}
node Pow(node x, ll m)
{
node ans;
memset(ans.a, 0, sizeof(ans.a));
for(int i = 0; i < (1<<n); i++) ans.a[i][i] = 1;
while(m)
{
if(m&1) ans = mul(ans, x);
x = mul(x, x);
m >>= 1;
}
return ans;
}
int main()
{
ll m;
scanf("%d %lld", &n, &m);
memset(c.a, 0, sizeof(c.a));
for(int i = 0; i < (1<<n); i++)
dfs(0, i, 0);
node ans = Pow(c, m);
printf("%lld\n", ans.a[(1<<n)-1][(1<<n)-1]);//输出状态全填到状态全填, m 列的方案数。
return 0;
}
ACM-ICPC 2018 焦作赛区网络预赛 L. Poor God Water
Step1 Problem:
有 n 个位置,每个位置可以填 0, 1, 2.
相邻三个数不能满足如下几个形式:
0, 0, 0
1, 1, 1
2, 2, 2
2, 1, 2
2, 0, 2
1, 2, 0
0, 1, 2
问你填满 n 位的方案数。
Step2 Ideas:
构造状态转移矩阵
枚举两位的所有状态,多加一位如果合法,就得到了两位状态到两位状态的转移。
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 12;
const int MOD = 1e9+7;
struct node
{
ll a[N][N];
};
node c, ans;
node mul(node x, node y)
{
node ans;
memset(ans.a, 0, sizeof(ans.a));
for(int k = 0; k < 9; k++)
{
for(int i = 0; i < 9; i++)
{
if(x.a[i][k])
for(int j = 0; j < 9; j++)
{
ans.a[i][j] += x.a[i][k]*y.a[k][j];
ans.a[i][j] %= MOD;
}
}
}
return ans;
}
node Pow(node x, ll m)
{
node ans;
memset(ans.a, 0, sizeof(ans.a));
for(int i = 0; i < 9; i++) ans.a[i][i] = 1;
while(m)
{
if(m&1) ans = mul(ans, x);
x = mul(x, x);
m >>= 1;
}
return ans;
}
int ok[10] = {111, 222, 0, 120, 21, 212, 202};
void init()
{
memset(c.a, 0, sizeof(c.a));
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
for(int k = 0; k < 3; k++)
{
int t = i*100+j*10+k, k1;
for(k1 = 0; k1 < 7; k1++) {
if(t == ok[k1]) break;
}
if(k1 == 7) c.a[i*3+j][j*3+k]++;//如果合法
}
}
}
}
int main()
{
int T;
ll n;
scanf("%d", &T);
init();
while(T--)
{
scanf("%lld", &n);
if(n < 3) {
if(n == 1) printf("3\n");
else printf("9\n");
continue;
}
ans = Pow(c, n-2);//矩阵是两位的状态到新的两位的状态,默认有两位了。
ll Ans = 0;
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
Ans += ans.a[i][j], Ans%=MOD;
}
}
printf("%lld\n", Ans);//因为矩阵转移都是合法状态,所以是所有状态的和。
}
return 0;
}