[ABC345D] Tiling 位运算の极致运用

[ABC345D] Tiling

原题解地址:Editorial by Kiri8128

神写法。

\(H \times W\) 的网格展开为 \(H \times (W + 1)\) 的序列, 每行多出来的一格表示换行。

W += 1;

\(F(a, b)\) 表示长为 \(a\),宽为 \(b\) 的矩形填满网格左上角的状态,直接给出公式,可以模拟检验正确性。

i128 F(int a, int b) {
	return (((i128)1 << a * W) - 1) / ((1 << W) - 1) * ((1 << b) - 1);
}

搜索过程:

  • 枚举每个矩形出现顺序。
  • 初状态 \(s = F(H, W - 1)\)
  • 二进制枚举每个矩形是否旋转。
  • \(x\) 为矩形的值,如果 \(x \ \& \ s = x\),那么 \(x\) 能填入,否则结束循环。
  • 当一个矩形能够填入时,更新左上角,用 \(lowbit\) 去掉 \(s\) 末尾的 \(0\)
  • \(s = 0\) 即找到一组合法解。

写法较于一些冗长的搜索显得极为优雅。

注意 next_permutation 是按字典序判的,因此先排序。

#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); ++ i)
#define per(i, a, b) for(int i = (a); i >= (b); -- i)
#define pb emplace_back
using namespace std;
using i128 = __int128;

int N, H, W;

i128 F(int a, int b) {
	return (((i128)1 << a * W) - 1) / ((1 << W) - 1) * ((1 << b) - 1);
}

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	cin >> N >> H >> W;
	W += 1;
	vector<pair<i128, i128>> a;
	rep(i, 1, N) {
		int x, y; cin >> x >> y;
		a.pb(F(x, y), F(y, x));
	}
	i128 S = F(H, W - 1);
	ranges::sort(a);
	do {
		for(int i = 0; i < 1 << N; ++ i) {
			i128 s = S;
			for(int j = 0; j < N; ++ j) {
				i128 x = (i >> j & 1) ? a[j].first : a[j].second;
				if((x & s) != x) break;
				if((s ^= x) == 0) {
					cout << "Yes";
					exit(0);
				}
				s /= s & -s;
			}
		}
	} while(ranges::next_permutation(a).found);
	cout << "No";
	return 0;
}
posted @ 2024-03-21 23:31  Lu_xZ  阅读(19)  评论(0编辑  收藏  举报