TOJ 1611 Moo University - Financial Aid -- 線段樹 + DP

題目鏈接:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1611

題目大意:給定c個二元組(x, y),要求從中選出n(n爲奇數)個,滿足這n個二元組的y之和不超過給定的f,而且x的中位數最大。輸出最大的中位數。

分析:先將二元組(記爲cow)按X排序,然後枚舉每個X作爲中位數。這時需要在X之前和X之後各選n / 2個數,因爲已經按X排序,不管怎麼選都可以滿足X是中位數的條件,所以只需要各自選取Y最小的n / 2個數,判斷它們的和是否滿足不超過f這個條件。如果滿足,X可行,否則不行。如果按X從大到小枚舉X,那麼遇到的第一個可行X即爲答案。剩下的問題在於如何高效地求出最小的n / 2個數的和。

可以用動態規劃解決這個問題。用一個長爲n / 2的數組a記錄以cow[i].X爲中位數時前面最小的n / 2個Y,同時用dp[i]表示這些數中的最大值的下標,那麼計算dp[i + 1]時,如果cow[i + 1].Y >= a[dp[i]],則dp[i + 1] = dp[i]。否則用cow[i + 1].Y替換a[dp[i]],同時計算出新的最大值下標也就是dp[i + 1]。這涉及單點更新及全區間的最大值查詢,用線段樹可以實現。至於求和,在計算dp[i]的時候順便就可以求出來了。對於cow[i].X之後的數,再進行一次同樣的操作。下面的代碼中的dp保存的就是n / 2個數的和,因爲最大值下標可以直接從線段樹中讀出,不需要另外存儲。求出dp之後再進行一次線性掃描就可以求出答案了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mp make_pair
#define X first
#define Y second
#define MEMSET(a, b) memset(a, b, sizeof(a))
using namespace std;

typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
typedef pair pii;
typedef vector vi;
typedef vi::iterator vi_it;
typedef map mii;
typedef priority_queue pqi;
typedef priority_queue, greater > rpqi;
typedef priority_queue pqp;
typedef priority_queue, greater > rpqp;

const int MAX_C = 100000 + 2;
const int MAX_N = 10000 + 2;
pii cow[MAX_C];
int a[MAX_N];
int dp[2][MAX_C];
int cnt;

struct
{
	int left;
	int right;
	int max_index;
	
	inline int mid() {
		return (left + right) >> 1;
	}
} st[MAX_N * 4];

void build(int l, int r, int idx)
{
	st[idx].left = l;
	st[idx].right = r;
	if (l == r) st[idx].max_index = cnt++;
	else {
		int mid = st[idx].mid(), lc = idx << 1, rc = lc | 1;
		build(l, mid, lc);
		build(mid + 1, r, rc);
		int lm = st[lc].max_index, rm = st[rc].max_index;
		st[idx].max_index = a[lm] > a[rm] ? lm : rm;
	}
}

void update(int pos, int idx)
{
	if (st[idx].left != st[idx].right) {
		int mid = st[idx].mid(), lc = idx << 1, rc = lc | 1;
		if (pos <= mid) update(pos, lc);
		else update(pos, rc);
		int lm = st[lc].max_index, rm = st[rc].max_index;
		st[idx].max_index = a[lm] > a[rm] ? lm : rm;
	}
}

int main(int argc, char *argv[])
{
//	freopen("D:\\in.txt", "r", stdin);
	int n, c, f, i;
	cin >> n >> c >> f;
	for (i = 0; i < c; ++i) scanf("%d%d", &cow[i].X, &cow[i].Y);
	
	//先處理n = 1時的特殊情況 
	if (n == 1) {
		int ans = -1;
		for (int j = 0; j < c; ++j) if (cow[j].Y <= f) ans = max(ans, cow[j].X);
		cout << ans << endl;
		return 0;
	}
	
	sort(cow, cow + c);
	
	//計算dp[0] 
	for (i = 0; i < (n >> 1); ++i) dp[0][n >> 1] += (a[i] = cow[i].Y);
	cnt = 0;
	build(0, (n >> 1) - 1, 1);
	for (; i < c - (n >> 1); ++i) {
		int mi = st[1].max_index;
		if (a[mi] > cow[i].Y) {
			dp[0][i + 1] = dp[0][i] - a[mi] + cow[i].Y;
			a[mi] = cow[i].Y;
			update(mi, 1);
		}
		else dp[0][i + 1] = dp[0][i];
	}
	
	//計算dp[1] 
	for (i = 0; i < (n >> 1); ++i) dp[1][c - (n >> 1) - 1] += (a[i] = cow[c - 1 - i].Y);
	cnt = 0;
	build(0, (n >> 1) - 1, 1);
	for (i = c - (n >> 1) - 1; i > (n >> 1); --i) {
		int mi = st[1].max_index;
		if (a[mi] > cow[i].Y) {
			dp[1][i - 1] = dp[1][i] - a[mi] + cow[i].Y;
			a[mi] = cow[i].Y;
			update(mi, 1);
		}
		else dp[1][i - 1] = dp[1][i];
	}
	
	for (i = c - (n >> 1) - 1; i >= (n >> 1); --i) if (dp[0][i] + dp[1][i] + cow[i].Y <= f) break;
	if (i >= (n >> 1)) cout << cow[i].X << endl;
	else cout << -1 << endl;
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章