P8189 [USACO22FEB] Redistributing Gifts G

模拟赛题,挺简单的。

知周所众,\(n\leq 18\) 大概率是状压(雾)。

首先发现实际上题目的很大一部分都没有用,实际上是在求:

求排列 \(b\),每个位置 \(i\),限制只能放 \(L_i\) 中的数。设 \(F(S)\) 表示对于下标集合 \(S\),只考虑 \(i \in S\) 的位置,\(i\in S, b_i\in S,b_i\in L_i\) 的方案数。则答案为 \(F(S) \cdot F(\complement_{[1,n]\cap \mathbb{Z}} S )\)

但是直接算 \(F\) 有点难,怎么办,注意到合并下标集合 \(S\)\(\{x\}\) 的答案时,我们可以做两件事情:

  • 可以让 \(b_x = x\),这样一定合法;

  • 在第一种方案的基础上,找到一个数 \(y\in S,b_y\in L_x,x\in L_y\),可以进行一次“交换”操作,交换 \(b_x\)\(b_y\)。这样也一定是合法的。

于是可以设计一种状态 \(f_{S,x}\) 表示只考虑下标 \(i\in S\) 的位置,钦定下标最小位置上 \(b_i=x\)这个位置不需要考虑 \(x \in L_i\) 的限制的方案数。

考虑转移,计算 \(S\) 时,枚举要把哪个数 \(x\) 放在下标最小的位置上,然后让 \(x\) 和最低位进行一次交换即可。

复杂度 \(O(2^n n^2)\)

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr inline int bit(const unsigned &x) {
	return 1 << x;
}
constexpr inline int dig(const int &x, const unsigned &k) {
	return (x >> k) & 1;
}
int a[20];
int pos[20];
int ac[20];
ll f[bit(20)][20];
ll g[bit(20)];
int n, q;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			cin >> a[j];
			pos[a[j]] = j;
		}
		for(int j = 1; j <= n; j++) {
			if(pos[j] <= pos[i])
			 	ac[i] |= bit(j - 1);
		}
	}
	g[0] = 1;
	for(int i = 1; i <= n; i++) {
		g[bit(i - 1)] = f[bit(i - 1)][i] = 1;
	}
	for(int S = 1; S < bit(n); S++) {
		if(__builtin_popcount(S) <= 1) continue;
		int cur = __builtin_ctz(S) + 1;
		ll del = g[S ^ bit(cur - 1)];
		f[S][cur] += del;
		g[S] += del;
		for(int i = cur + 1; i <= n; i++) {
			if(!(S & bit(i - 1))) continue;
			int can = ac[i] & (S ^ bit(i - 1));
			for(int j = 1; j <= n; j++) {
				if(!dig(can, j - 1)) continue;
				ll del = f[S ^ bit(i - 1)][j];
				f[S][i] += del;
				if(dig(ac[cur], i - 1)) g[S] += del;
			}
		}
	}
	cin >> q;
	for(int i = 1; i <= q; i++) {
		char s[20];
		cin >> s;
		int S = 0;
		for(int j = 1; j <= n; j++) {
			if(s[j - 1] == 'H') S |=  bit(j - 1);
		}
		cout << (g[S] * g[(bit(n) - 1) ^ S]) << '\n';
	}
}
posted @ 2024-01-20 12:51  N2MENT  阅读(19)  评论(0)    收藏  举报