【文文殿下】CF1029F Multicolored Markers
这道题考场上卡了文文相当长的时间,所以写个题解泄泄愤QAQ
题意:给你$a$块红瓷砖,$b$块白瓷砖,在一个无限大的地上拼装,要求整体是一个矩形,并且至少有一种颜色是一个矩形,求最小周长。
题解:
首先,我们知道,当面积一定时,矩形的形状越接近正方形,周长越小。
很显然的想到,我们可以给总数$tot$=$a$+$b$开个方,然后去找一个最接近的矩形出来。
但事实上这个方法是错误的:你无法保证至少有一种颜色的是一个矩形。
我们首先,要分情况讨论,让$a$是矩形还是让$b$是矩形。当然,我们讨论每个的时候,不约束另一个的形状。
假设我们保证$a$是矩形,那么我们就要首先找出从$1$到$\sqrt{a}$之间所有的$a$的因数,来找到$a$矩形的较短边。
然后再找总数的较短边。
我们每找到一个总数的较短边$x$,就要在$a$的因数中,找到最大的一个小于$x$的数字。这一步可以用一个$splay$维护,当然由于是静态的,也可以二分查找。
但事实上,有一个显而易见的规律:如果我们从小到大枚举$tot$的因数,那么对应的$a$的因数是单调的。所以我们利用这个单调性,缩短到$O(a)$.
然后对$b$ ,Once More! 找两者的最优解
参考代码
#include <bits/stdc++.h> #define forn(i, n) for (int i = 0; i < int(n); i++) typedef long long li; using namespace std; const int N = 1000 * 1000; int lens[N]; int k; li solve(li a, li b){ k = 0; for (li i = 1; i * i <= b; ++i) if (b % i == 0) lens[k++] = i; li ans = 2 * (a + b) + 2; li x = a + b; int l = 0; for (li i = 1; i * i <= x; ++i){ if (x % i == 0){ while (l + 1 < k && lens[l + 1] <= i) ++l; if (b / lens[l] <= x / i) ans = min(ans, (i + x / i) * 2); } } return ans; } int main() { li a, b; scanf("%lld%lld", &a, &b); printf("%lld\n", min(solve(a, b), solve(b, a))); return 0; }