Codeforces 1462F. The Treasure of The Segments (贪心&树状数组)

F. The Treasure of The Segments

time limit per test

3 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

Polycarp found nn segments on the street. A segment with the index ii is described by two integers lili and riri — coordinates of the beginning and end of the segment, respectively. Polycarp realized that he didn't need all the segments, so he wanted to delete some of them.

Polycarp believes that a set of kk segments is good if there is a segment [li,ri][li,ri] (1≤i≤k1≤i≤k) from the set, such that it intersects every segment from the set (the intersection must be a point or segment). For example, a set of 33 segments [[1,4],[2,3],[3,6]][[1,4],[2,3],[3,6]] is good, since the segment [2,3][2,3] intersects each segment from the set. Set of 44 segments [[1,2],[2,3],[3,5],[4,5]][[1,2],[2,3],[3,5],[4,5]] is not good.

扫描二维码关注公众号,回复: 12168478 查看本文章

Polycarp wonders, what is the minimum number of segments he has to delete so that the remaining segments form a good set?

Input

The first line contains a single integer tt (1≤t≤2⋅1051≤t≤2⋅105) — number of test cases. Then tt test cases follow.

The first line of each test case contains a single integer nn (1≤n≤2⋅1051≤n≤2⋅105) — the number of segments. This is followed by nn lines describing the segments.

Each segment is described by two integers ll and rr (1≤l≤r≤1091≤l≤r≤109) — coordinates of the beginning and end of the segment, respectively.

It is guaranteed that the sum of nn for all test cases does not exceed 2⋅1052⋅105.

Output

For each test case, output a single integer — the minimum number of segments that need to be deleted in order for the set of remaining segments to become good.

Example

input

Copy

4
3
1 4
2 3
3 6
4
1 2
2 3
3 5
4 5
5
1 2
3 8
4 5
6 7
9 10
5
1 5
2 4
3 5
3 8
4 8

output

Copy

0
1
2
0

题目大意:

有N条线段,一个线段集合是好的,当且仅当集合中至少存在一条线段能够和集合中所有的线段相交,现在让你删除最少的线段,使得剩下的线段能够构成好的集合,输出最小删除次数。

解法:

问题转换成问你每个线段和多少条线段相交。

首先离散化一下,我们只看每个线段的端点,对于某条线段,区间中出现多少端点代表有多少相交线段,注意如果某条线段两个端点都在里面,答案就不对了,所以我们需要分类,先加左端点,再加右端点。这时候思路已经出来了,按照左右端点各排下序,先访问下每个线段包含了多少左端点,再倒着遍历看包含了多少右端点,中间用树状数组维护即可。

Accepted code

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair <int, int>
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 2e5 + 100;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }

struct node
{
	int l, r;
	bool operator < (const node &oth) const {
		if (r == oth.r)
			return l < oth.l;
		else
			return r < oth.r;
	}
}a[N];
int s[N * 2], ans[N], n, sz;
vector <int> ver;

void Init() {
	for (int i = 1; i <= sz; i++)
		s[i] = 0;
	ver.clear();
}
void Add(int x, int v) {
	while (x <= sz)
		s[x] += v, x += lowbit(x);
}
int Ask(int x) {
	int tot = 0;
	while (x)
		tot += s[x], x -= lowbit(x);
	return tot;
}
int Rg_Ask(int l, int r) {
	return Ask(r) - Ask(l - 1); 
}

int main()
{
#ifdef OlaMins
	freopen("D:/input.txt", "r", stdin);
	//freopen("D:/output.txt", "w", stdout);
#endif

	int T; cin >> T;
	while (T--) {
		sc("%d", &n);
		Init();

		for (int i = 1; i <= n; i++) {
			int l, r;
			sc("%d %d", &l, &r);
			ver.push_back(l), ver.push_back(r);
			a[i] = { l, r }, ans[i] = 0;
		}
		sort(ALL(ver));
		ver.erase(unique(ALL(ver)), ver.end());  // 离散化
		sz = SZ(ver);

		for (int i = 1; i <= n; i++) {
			a[i].l = lower_bound(ALL(ver), a[i].l) - ver.begin() + 1;
			a[i].r = lower_bound(ALL(ver), a[i].r) - ver.begin() + 1;
		}
		sort(a + 1, a + n + 1);

		for (int i = 1; i <= n; i++) {  // 先左
			int l = a[i].l, r = a[i].r;
			ans[i] = Rg_Ask(l, r);
			Add(r, 1);
		}

		for (int i = 1; i <= sz; i++)
			s[i] = 0;
		int mi = INF;
		for (int i = n; i >= 1; i--) {   // 后右
			int l = a[i].l, r = a[i].r;
			ans[i] += Rg_Ask(l, r);
			Min(mi, n - ans[i]);
			Add(l, 1);
		}
		printf("%d\n", mi - 1);
	}
	return 0; // 改数组大小!!!用pair改宏定义!!!
}

猜你喜欢

转载自blog.csdn.net/weixin_43851525/article/details/111384943
今日推荐