题解:P16480 [GKS 2014 #A] Cut Tiles

· · 题解

为啥题解区都这么复杂。

大瓷砖可以分裂为小的瓷砖。建议画图来理解过程。

::::info[分裂过程]

:::: 可以想到从大到小做。枚举 $j$ 代表现在处理到了第 $j$ 位,记录现在有多少个能放 $2^j$ 的位置 $rst$,如果 $rst>cnt_j$,那说明可以直接放下,否则还需要多买一些大瓷砖。 那么现在就要快速计算买一块大瓷砖可以做出多少个大小为 $2^j$ 的空位。由上文分裂过程可知,原来没用完的空位可以分裂为现在的空位。但是也可能可以截一些之前没用完的边角料下来用,这个要如何计算呢? 一个不难发现的事实是,每当 $m$ 这一位是 $1$ 的时候,就会出现之前大的方块没填满的一条边角料现在可以用了。而这个边角料可以放下 $2 \times \frac{m}{2^j}-1$ 个 $j \times j$ 的方块。这一块建议画图理解或是看别的题解里面的图辅助理解。 于是这样就可以快速算出 $m \times m$ 的瓷砖可以放下 $k$ 个 $j \times j$ 的瓷砖,不够的话多买 $\lfloor \frac{cnt_j-rst}{k} \rfloor$ 个大瓷砖即可。 时间复杂度 $O(n + \log m)$。 可以让所有的数值对 $n$ 取 $\min$,这样不会影响答案并且不用担心爆 long long。 其实看代码还是很明白的。 ```cpp #include <bits/stdc++.h> #define ull unsigned long long #define int long long #define fi first #define se second #define pb emplace_back #define mk make_pair using namespace std; typedef pair<int, int> pii; const int N = 2e5 + 10, inf = 1e18, M = 2e6 + 10, mod = 1e9 + 7; const double eps = 1e-8; int n, m, a[N], cnt[35]; void solve(int csd) { cin >> n >> m; int K = __lg(m); memset(cnt, 0, sizeof cnt); for(int i = 1; i <= n; i++) cin >> a[i], cnt[a[i]]++; int rst = 0, all = 0, k = 1; for(int j = K; ~j; j--) { if(j != K && (m >> j) & 1) { int del = (m >> j) * 2 - 1; rst = min(rst + all * del, n), k = min(k + del, n); } if(rst < cnt[j]) { int res = (cnt[j] - rst + k - 1) / k; rst += res * k, all += res; } rst -= cnt[j]; k *= 4, rst *= 4; k = min(k, n), rst = min(rst, n); } cout << "Case #" << csd << ": " << all << "\n"; } signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); int T; cin >> T; for(int t = 1; t <= T; t++) solve(t); return 0; } ```