Atcoder - 091C 扫描线 and Atcoder - 062D 优先队列 + 前后缀和

<Atcoder - 091C> 扫描线

https://atcoder.jp/contests/abc091/tasks/arc092_a

题意:

有n个红点,n个蓝点,所选的红点和蓝点能够成友好的条件是:红点的横纵座标分别小于蓝点的横纵座标。取过的点不能重复取,问最多能构成几对友好。

思路:

定义结构体,将所有红点蓝点统一按照x升序排列。然从后往前扫,碰到蓝点就传入set,碰到红点时,就利用set的upper_bound返回纵座标大于该红点纵座标且离他最近的蓝点,因为x升序过了,故只看y,用过的蓝点清除出set即可。

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxx = 2e2 + 7;
const int Inf = 1 << 30;
const LL INF = 1LL << 60;
int n, ans;
set <int> sst;
struct PP {
	int x, y;
	bool color;
	friend bool operator < (PP u, PP v) {
		return u.x < v.x;
	}
} p[maxx];

void Init() {
	cin >> n;
	for(int i = 1; i <= n; i++) {
		cin >> p[i].x >> p[i].y;
		p[i].color = 0;
	}
	for(int i = 1; i <= n; i++) {
		cin >> p[i + n].x >> p[i + n].y;
		p[i + n].color = 1;
	}
	sort(p + 1, p + 2 * n + 1);
}

void Scan() {
	set <int>::iterator id;
	for(int i = 2 * n; i >= 1; i--) {
		if(p[i].color) sst.insert(p[i].y);
		else {
			id = sst.upper_bound(p[i].y);
			if(id != sst.end()) {
				ans++;
				sst.erase(id);
			}
		}
	}
	cout << ans << endl;
}

int main() {
	Init();
	Scan();
	return 0;
}

<Atcoder - 062D> 优先队列 + 前后缀和

https://atcoder.jp/contests/abc062/tasks/arc074_b

题意:

从3n个数中精确删除n项,使得剩下的2n个数前n项和与后n项和的差值最大,求这个最大差值。

思路:

这题类似于一个插板法,无论怎么删除,原来元素的相对位置是不发生改变的。原来3n个数的序列的前n项都不可能进入新的2n个数的序列的后n项,同理,原来3n个数的序列的后n项都不可能进入新的2n个数的序列的前n项。那么不断用中间的n项去置换两边的n项,便能达到删除的目的,被中间的n项置换出来的数就相当于被删除了。那么置换条件显然是把中间n项中大的数往前置换,小的数往后置换,这样才能使得前n项和后n项的差值最大。那么我们可以在原始的3n项的序列内,前n项压进升序(即队头最小)的优先队列que,后n项压进降序(即队头最大)的优先队列qua。记前n项的前缀和为suml[n] = s1,后n项的后缀和为sumr[n * 2 + 1] = s2。那么我们研究中间的n项如何置换:

1)令l = n + 1,r = 2n,在 l <= r 内,碰到比que.top()大的,就发生置换,改变原来的队头(比原来那个队头大的不是直接成为新的队头,把它压进队列,让队头变成新的这n个数中一个新的最小值),改变前缀和s1。

2)令l = n + 1,r = 2n,在 l <= r 内,碰到比qua.top()小的,就发生置换,改变原来的队头(比原来那个队头小的不是直接成为新的队头,把它压进队列,让队头变成新的这n个数中一个新的最大值),改变后缀和s2。

对于中间的n项,由于不管怎么删除相对位置都不会改变(应该谁在谁前面,就谁在谁前面),一定是前x个置换进入前n项,后n-x个置换进入后n项(当然也可能不发生置换),这也是记录前后缀和的原因。所以对于 n <= i <= 2n,相当于在 i 这个位置插一个隔板,隔板之前的元素向前发生置换,隔板之后的元素向后发生置换。维护前后缀和suml[i] - sumr[i + 1]的最大值就是答案。

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 3e5 + 7;
const int Inf = 1 << 30;
const ll INF = 1ll << 60;
int n;
ll a[maxx];
ll suml[maxx], sumr[maxx];
ll s1, s2;
priority_queue <ll, vector<ll>, less<ll> > qua; //降序, 队头大
priority_queue <ll, vector<ll>, greater<ll> > que; //升序, 队头小

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n * 3; i++) {
		scanf("%lld", &a[i]);
		if(i <= n) {
			s1 += a[i];
			que.push(a[i]);
		}
		else if(i >= 2 * n + 1) {
			s2 += a[i];
			qua.push(a[i]);
		}
	}
	suml[n] = s1; //前n项和
	sumr[n * 2 + 1] = s2; //后n项和
	int l = n + 1, r = 2 * n;
	//只能精确删除n项, 无论怎么删, 后n项都不可能进到前n项去
	//故让中间的n项去往两边置换
	//中间的n项必然是前x个置换到前n项内, 后n-x个置换到后n项内(或者不发生置换)
	//故分别维护前缀和、后缀和, 前缀和与后缀和的差维护一个最大值
	while(l <= r) {
		if(a[l] > que.top()) { //每次置换出最小的队头
			s1 += a[l];
			s1 -= que.top();
			que.pop();
			que.push(a[l]);
		}
		suml[l] = s1; //维护前缀和
		l++;
	}
	l = n + 1, r = 2 * n;
	while(l <= r) {
		if(a[r] < qua.top()) { //每次置换出最大的队头
			s2 += a[r];
			s2 -= qua.top();
			qua.pop();
			qua.push(a[r]);
		}
		sumr[r] = s2; //维护后缀和
		r--;
	}
	ll ans = -INF;
	for(int i = n; i <= n * 2; i++) ans = max(ans, suml[i] - sumr[i + 1]);
	printf("%lld\n", ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章