CF260E - Dividing Kingdom
D e s c r i p t i o n \mathrm{Description} Description
给定 n n n 个点 ( x i , y i ) (x_i,y_i) (xi,yi) 和长度为 9 9 9 的数列 a a a,满足 ∑ i = 1 n a i = n \sum_{i=1}^na_i=n ∑i=1nai=n。通过 2 2 2 条平行于 x x x 轴的直线和 2 2 2 条平行于 y y y 轴的直线,将平面划分成 9 9 9 个部分,第 i i i 个部分点的数量记作 b i b_i bi。
输出一组解满足 b b b 是 a a a 的排列,或报告无解。
9 ≤ n ≤ 1 0 5 , 1 ≤ x i , y i ≤ 1 0 9 9\le n\le 10^5,1\le x_i,y_i\le 10^9 9≤n≤105,1≤xi,yi≤109
S o l u t i o n \mathrm{Solution} Solution
观察到排列二字, a a a 的长度还是 9 9 9,不难想到将 a a a 进行全排列,对于每一种情况分别计算。
上图给出了划分,其中标号为 i i i 表示格子内点数为 a i a_i ai。后面便需要对于 a a a 的一种排列,初步确定出 红色线 \color{red}{红色线} 红色线 和 蓝色线 \color{blue}{蓝色线} 蓝色线 的位置。不难发现,第 1 1 1 根红线下面点数应为 a 1 + a 4 + a 7 a_1+a_4+a_7 a1+a4+a7,第 1 1 1 根红线至第 2 2 2 根内点数应为 a 2 + a 5 + a 8 a_2+a_5+a_8 a2+a5+a8(对于蓝线也同理,这里不过多赘述)。
故,通过二分可以快速确定红蓝线的位置。不过,按照如上的确定方式,一定能保证对应块满足条件吗?答案是否定的。还需要判断每个小方格内是否点数是匹配的,即相当于求若干个矩形内的点数,与 P2163 [SHOI2007] 园丁的烦恼 有异曲同工之处(这里不再细说)。
当你写完提交后,发现不是 M L E \mathrm{MLE} MLE,就是 T L E \mathrm{TLE} TLE。分析代码消耗时间最多处,能知晓是计算 9 9 9 块常数太大,考虑优化。其实,由于前面特殊的划分方式,其实只需要计算 1 , 3 , 5 , 7 , 9 1,3,5,7,9 1,3,5,7,9 块即可,大大降低了时空复杂度(这里请读者自行理解)。
综上所述,即可通过理论时间复杂度为 O ( n log n + 9 ! log n ) O(n\log n+9!\log n) O(nlogn+9!logn) 的算法通过该题,不过由于常数过大,可以近似看做 O ( n log 2 n + 9 ! log n ) O(n\log ^2n+9!\log n) O(nlog2n+9!logn)。
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 2e5 + 10;
int n, m, k;
PII pnt[N];
int a[10], col[N], lin[N], qry[N * 20], idx, ans[4][N << 1];
std::vector<int> dct;
struct Query {
int x, y, sign, id;
bool operator< (const Query &tmp)const {
if (y == tmp.y) {
if (!sign) return 1;
else if (!tmp.sign) return 0;
return x < tmp.x;
}
return y < tmp.y;
}
}q[N * 40];
int tr[N];
inline void add(int x, int d) { for (int i = x; i <= dct.size(); i += (i & -i)) tr[i] += d; }
inline int sum(int x) {
int res = 0;
for (int i = x; i; i -= (i & -i)) res += tr[i];
return res;
}
inline int find(int x) {
return lower_bound(dct.begin(), dct.end(), x) - dct.begin() + 1;
}
inline int binary(int aim, int v, int tmp[]) {
int l = 1, r = dct.size();
while (l < r) {
int mid = l + r >> 1;
if (tmp[mid] - v >= aim) r = mid;
else l = mid + 1;
}
if (tmp[r] - v != aim) return -1;
return r;
}
inline void add_query(int x1, int y1, int x2, int y2, int id) {
q[ ++ idx] = {x2, y2, 1, id};
if (x1 > 1) q[ ++ idx] = {x1 - 1, y2, -1, id};
if (y1 > 1) q[ ++ idx] = {x2, y1 - 1, -1, id};
if (x1 > 1 && y1 > 1) q[ ++ idx] = {x1 - 1, y1 - 1, 1, id};
}
signed main() {
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i ++ ) {
cin >> pnt[i].fi >> pnt[i].se;
dct.push_back(pnt[i].fi), dct.push_back(pnt[i].se);
}
sort(dct.begin(), dct.end());
dct.erase(unique(dct.begin(), dct.end()), dct.end());
for (int i = 1; i <= n; i ++)
q[ ++ idx] = {find(pnt[i].fi), find(pnt[i].se), 0, 0};
for (int i = 1; i <= 9; i ++)
cin >> a[i];
sort(a + 1, a + 10);
for (int i = 1; i <= n; i ++)
col[find(pnt[i].fi)] ++, lin[find(pnt[i].se)] ++;
for (int i = 1; i <= dct.size(); i ++)
col[i] += col[i - 1], lin[i] += lin[i - 1];
do {
int l1, l2, c1, c2;
l1 = binary(a[1] + a[2] + a[3], 0, col);
if (l1 == -1) continue;
l2 = binary(a[4] + a[5] + a[6], col[l1], col);
if (l2 == -1) continue;
c1 = binary(a[7] + a[4] + a[1], 0, lin);
if (c1 == -1) continue;
c2 = binary(a[8] + a[5] + a[2], lin[c1], lin);
if (c2 == -1) continue;
ans[0][ ++ k] = l1, ans[1][k] = l2, ans[2][k] = c1, ans[3][k] = c2;
qry[ ++ m] = a[3], add_query(1, c2 + 1, l1, dct.size(), m);
qry[ ++ m] = a[9], add_query(l2 + 1, c2 + 1, dct.size(), dct.size(), m);
qry[ ++ m] = a[5], add_query(l1 + 1, c1 + 1, l2, c2, m);
qry[ ++ m] = a[1], add_query(1, 1, l1, c1, m);
qry[ ++ m] = a[7], add_query(l2 + 1, 1, dct.size(), c1, m);
}while (next_permutation(a + 1, a + 10));
stable_sort(q + 1, q + 1 + idx);
for (int i = 1; i <= idx; i ++)
if (!q[i].sign)
add(q[i].x, 1);
else
qry[q[i].id] -= q[i].sign * sum(q[i].x);
for (int i = 1; i <= k; i ++) {
bool flg = 1;
for (int j = (i - 1) * 5 + 1; j <= i * 5; j ++)
flg &= (!qry[j]);
if (flg) {
printf("%.1f %.1f\n%.1f %.1f\n", dct[ans[0][i] - 1] + 0.5, dct[ans[1][i] - 1] + 0.5, dct[ans[2][i] - 1] + 0.5, dct[ans[3][i] - 1] + 0.5);
return 0;
}
}
cout << -1 << endl;
return 0;
}