「清新题精讲」CF260E - Dividing Kingdom

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 9n105,1xi,yi109

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;
}
  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值