题解:P9762 [ROIR 2021 Day 1] 分割数表
思路
我们首先化简算出数表的式子。
横切原式
\[ \\ \sum_{i=1}^{n} \sum_{j=1}^{k}i\times m -m + j
\\ =((n^2-1)km + (k^2+1)n)\div2
\]
竖切原式
\[ \\ \sum_{i=1}^{k} \sum_{j=1}^{n}i\times m -m + j
\\ =((k^2-1)m^2 + (m+1)km)\div 2
\]
考虑二分横/竖切时两部分最平均的位置。注意可能有偏差,记得对二分结果进行 check。
代码如下
#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
#define rep(i, l, r) for(int i = l; i <= r; ++ i)
#define per(i, r, l) for(int i = r; i >= l; -- i)
int Q, n, m;
int check(int n, int m, int m1)
{
return ((n - 1) * n * m * m1 + (m + 1) * n * m) / 2;
}
void work()
{
int l = 1, r = m, sum = check(n, m, m), ans1 = 1e19, ans2 = 1e19, pos1 = 1, pos2 = 1;
for(; l <= r;)
{
int mid = (l + r) >> 1;
int k1 = check(n, mid - 1, m), k2 = sum - k1;
if(k1 < k2) l = mid + 1;
else r = mid - 1;
if(max(k1, k2) < ans1) ans1 = max(k1, k2), pos1 = mid;
}
int mid = l - 1;
int k1 = check(n, mid - 1, m), k2 = sum - k1;
if(max(k1, k2) <= ans1) ans1 = max(k1, k2), pos1 = mid;
l = 1, r = n;
for(; l <= r;)
{
int mid = (l + r) >> 1;
int k1 = check(mid - 1, m, m), k2 = sum - k1;
if(k1 < k2) l = mid + 1;
else r = mid - 1;
if(max(k1, k2) < ans2) ans2 = max(k1, k2), pos2 = mid;
}
mid = l - 1;
k1 = check(mid - 1, m, m), k2 = sum - k1;
if(max(k1, k2) <= ans2) ans2 = max(k1, k2), pos2 = mid;
if(ans1 <= ans2) printf("V %lld\n", pos1);
else printf("H %lld\n", pos2);
}
main()
{
scanf("%lld", &Q);
for(; Q; -- Q)
{
scanf("%lld%lld", &n, &m);
work();
}
return 0;
}