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;
}
posted @ 2024-03-23 16:31  -wryyy-  阅读(15)  评论(0编辑  收藏  举报