P8756 [蓝桥杯 2021 省 AB2] 国际象棋 题解
设计状态什么的就不讲了,这里是对其它题解的优化。
怎么优化呢,我们可以知道的是我们只要明确了当前行的状态,上一行的可选集就是知道的,如果我们明确了当前行以及上一行的状态,那么上上行的可选集就是知道的,于是我们就可以使用二进制子集枚举来写,这样就减去了全部不合法的枝叶,我们可以保证遍历到的三行的状态都是合法的。
当然要这样写就需要先预处理出某一个状态会攻击到的状态,也不难。
最后加上滚动数组并且不用 ll
就可以喜提最优解(204ms)。
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define ll long long
#define ull unsigned long long
#define m_p make_pair
#define m_t make_tuple
#define inf (0x7f7f7f7f)
#define N 300010
using namespace std;
using namespace __gnu_pbds;
const ll MOD = 1e9 + 7;
int atk[100][2], bitc[100];
int dp[2][80][80][30];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m, K, rn, x1, x2, con, num, nw = 0;
cin >> n >> m >> K;
rn = 1 << n;
--rn;
for (int ic = 1; ic <= rn; ic++)
for (int j = 0; j < n; j++)
if ((1 << j) & ic)
{
++bitc[ic];
x1 = j - 1;
x2 = j + 1;
if (x1 >= 0)
atk[ic][1] |= (1 << x1);
if (x2 < n)
atk[ic][1] |= (1 << x2);
--x1;
++x2;
if (x1 >= 0)
atk[ic][0] |= (1 << x1);
if (x2 < n)
atk[ic][0] |= (1 << x2);
}
dp[0][0][0][0] = 1;
int bc1, bc2;
for (int i = 1; i <= m; i++)
{
nw ^= 1;
memset(dp[nw], 0, sizeof dp[nw]);
for (int c3 = 0; c3 <= rn; c3++)
{
bc2 = rn ^ atk[c3][0];
num = bitc[c3];
for (int c2 = bc2;; c2 = (c2 - 1) & bc2)
{
bc1 = rn ^ (atk[c3][1] | atk[c2][0]);
for (int c1 = bc1;; c1 = (c1 - 1) & bc1)
{
for (int k = K; k >= num; k--)
dp[nw][c3][c2][k] = (1ll*dp[nw][c3][c2][k] + 1ll*dp[nw ^ 1][c2][c1][k - num]) % MOD;
if (!c1)
break;
}
if (!c2)
break;
}
}
}
int ans = 0;
for (int i = 0; i <= rn; i++)
for (int j = 0; j <= rn; j++)
ans = (1ll*ans + 1ll*dp[nw][i][j][K]) % MOD;
cout << ans;
return 0;
}