1. 程式人生 > 其它 >Codeforces 1462F. The Treasure of The Segments (貪心&樹狀陣列)

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 foundnnsegments on the street. A segment with the indexiiis described by two integersliliandriri— 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 ofkksegments 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 apoint or segment). For example, a set of33segments[[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 of44segments[[1,2],[2,3],[3,5],[4,5]][[1,2],[2,3],[3,5],[4,5]]is not good.

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 integertt(1≤t≤2⋅1051≤t≤2⋅105)— number of test cases. Thentttest cases follow.

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

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

It is guaranteed that the sum ofnnfor all test cases does not exceed2⋅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改巨集定義!!!
}