2020 Niuke Duo School's second H-Happy Triangle (dynamic opening line segment tree & SET)

 

Time limit: C/C++ 2 seconds, other languages ​​4 seconds
Space limit: C/C++ 262144K, other languages ​​524288K
64bit IO Format: %lld

Title description

Given a multiset MS{MS}MS and q{q}q operations. MS{MS}MS is empty initailly, and operations are in three types, which are as follows:
1. insert an element x{x}x into MS{MS}MS
2. erase an element x{x}x from MS{MS}MS
3. given an integer x{x}x, determine whether you can choose two elements a,b{a, b}a,b in MS{MS}MS that three segments of length a,b,x{a, b, x}a,b,x can make a nondegenerate triangle.

Enter description:

 

The first line contains an integer q (1≤q≤2×105)q~(1\leq q\leq 2\times 10^5)q (1≤q≤2×105), denoting the number of operations.
Following q{q}q lines each contains two integers op,x (op∈{1,2,3},1≤x≤109)op, x~(op \in \{1,2,3\}, 1\leq x\leq 10^9)op,x (op∈{1,2,3},1≤x≤109), denoting an operation
1. if op=1{op = 1}op=1, insert an element x{x}x into MS{MS}MS
2. if op=2{op = 2}op=2, erase an element x{x}x from MS{MS}MS, it's guaranteed that there is at least one x{x}x in MS{MS}MS currently
3. if op=3{op = 3}op=3, determine whether you can choose two elements a,b{a, b}a,b in MS{MS}MS that three segments of length a,b,x{a, b, x}a,b,x can make a triangle

Output description:

For each query, print one line containing a string "Yes" if the answer is yes or "No" if the answer is no.

Example 1

enter

Copy 8 1 1 3 1 1 1 3 2 3 1 1 2 2 1 3 1

8
1 1
3 1
1 1
3 2
3 1
1 2
2 1
3 1

Output

Recovery No No Yes No

No
No
Yes
No

Main idea:

For a set, there are two operations of adding number X and deleting number X. At the same time, there will be a query operation to ask whether there are two numbers a and b in the set to form a triangle with the current query X.

solution:

Assuming that the three sides a, b, and c of the triangle are arranged in a non-decreasing order, a+b>c can be known according to the nature of the triangle.

1. If X is c, obviously a and b are as large as possible;

2. If X is b, obviously a is as large as possible and c is as small as possible;

3. If X is a, choose among all the numbers larger than a, the two numbers with the smallest difference between adjacent numbers are better.

In summary, use multiset and map to maintain the first two cases, and use a line segment tree to maintain the difference between two adjacent numbers (x is large, and dynamic opening is good). For some cases, special judgment is required, for example, it can form an equilateral triangle, or the cases listed by X have no predecessor, successor, etc.... The details are still quite a lot, and it has been adjusted for a long time.

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 = 1e6 + 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; }

int mi[N * 60], ls[N * 60], rs[N * 60];
unordered_map <int, int> rt, mp;
multiset <int> st;
int idx, n = 1e9;

void Pushdown(int o) {
	if (!ls[o])
		ls[o] = ++idx, mi[idx] = INF;
	if (!rs[o])
		rs[o] = ++idx, mi[idx] = INF;
}
void Update(int o, int L, int R, int x, int val) {
	if (L == R && L == x) {
		mi[o] = val;
		return;
	}
	Pushdown(o);
	int mid = (L + R) >> 1;
	if (mid >= x)
		Update(ls[o], L, mid, x, val);
	else
		Update(rs[o], mid + 1, R, x, val);
	mi[o] = min(mi[ls[o]], mi[rs[o]]);
}
int Ask(int o, int L, int R, int l, int r) {
	if (L >= l && R <= r)
		return mi[o];
	Pushdown(o);
	int mid = (L + R) >> 1, ans = INF;
	if (mid >= l)
		Min(ans, Ask(ls[o], L, mid, l, r));
	if (mid < r)
		Min(ans, Ask(rs[o], mid + 1, R, l, r));
	return ans;
}
bool Bigest(int x) {
	if (mp[x]) {  // 有x
		auto pre = st.lower_bound(x);
		if (pre == st.begin())
			return false;
		return true;
	}
	else {  // 无x
		auto pre = st.lower_bound(x);
		if (pre == st.begin())  // 无前驱
			return false;
		pre--;
		if (pre == st.begin()) // 无前驱1
			return false;
		auto pre1 = pre;
		pre1--;
		if (*pre1 + *pre > x)
			return true;
		return false;
	}
}
bool Pre(int x) {
	auto pre = st.lower_bound(x);
	if (pre == st.begin()) // 无前驱
		return false;
	return true;
}
bool Nxt(int x, int &ans) {
	auto nxt = st.upper_bound(x);
	if (nxt == st.end())   // 无后继
		return false;
	ans = *nxt;
	return true;
}
bool Mid(int x) {
	if (mp[x]) {  // 有x
		int nxt;
		if (Pre(x))
			return true;
		if (Nxt(x, nxt)) {
			if (x + x > nxt)
				return true;
		}
		return false;
	}
	else { // 无x
		auto nxt = st.upper_bound(x);  // 后继
		if (nxt == st.end())
			return false;
		auto pre = nxt;
		if (pre == st.begin())  // 前驱
			return false;
		pre--;
		if (*pre + x > *nxt)
			return true;
		return false;
	}
}
bool Smallest(int x) {
	if (mp[x]) {
		auto nxt = st.upper_bound(x);
		if (nxt == st.end())
			return false;
		if (2 * x > *nxt)
			return true;
	}
	int c = Ask(rt[1], 1, n, x + 1, n);
	if (c < x)
		return true;
	return false;
}

int main()
{
	rt[1] = ++idx, mi[1] = INF;   // 动态开点
	int q;
	cin >> q;
	while (q--) {
		int op, x, y;
		sc("%d %d", &op, &x);
		if (op == 1) {              // 加入点X
			mp[x]++, st.insert(x);
			if (mp[x] == 1) {       // 是集合中唯一的X
				auto it = st.lower_bound(x);
				if (it == st.begin())    // 无前驱
					Update(rt[1], 1, n, x, INF);
				else {  // 有前驱,更新x差值
					it--;
					Update(rt[1], 1, n, x, x - *it);
				}

				it = st.upper_bound(x); // 后继
				if (it != st.end()) {
					if (mp[*it] == 1)
						Update(rt[1], 1, n, *it, *it - x);
				}
			}
			else // 差值为0
				Update(rt[1], 1, n, x, 0);
		}
		else if (op == 2) {    // 删除x
			auto it = st.lower_bound(x); 
			st.erase(it), mp[x]--;

			if (mp[x] == 1) {  // 找前驱
				auto it = st.lower_bound(x);
				if (it == st.begin()) // 无前驱
					Update(rt[1], 1, n, x, INF);
				else {
					it--;
					Update(rt[1], 1, n, x, x - *it);
				}
			}
			else if (!mp[x]) {
				Update(rt[1], 1, n, x, INF);
				if (st.empty())
					continue;
				auto it = st.upper_bound(x);
				if (it == st.end())  // 无后继
					continue;
				if (it == st.begin()) { // 后继且无前驱
					if (mp[*it] == 1)
						Update(rt[1], 1, n, *it, INF);
				}
				else if (mp[*it] == 1) {
					auto pre = it; pre--;
					Update(rt[1], 1, n, *it, *it - *pre);
				}
			}
		}
		else {
			if (SZ(st) <= 1) {
				puts("No");
				continue;
			}
			if (mp[x] >= 2) {
				puts("Yes");
				continue;
			}
			if (Bigest(x))   // X当作C
				puts("Yes");
			else if (Mid(x))   // X当作b
				puts("Yes");
			else if (Smallest(x))  // X当作a
				puts("Yes");
			else
				puts("No");
		}
	}
	return 0;  // 改数组大小!!!用pair记得改宏定义!!!
}

 

Guess you like

Origin blog.csdn.net/weixin_43851525/article/details/108777920