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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章