思維+優先隊列維護前n個數和的最小值 [TJOI2013]獎學金(洛谷 P3963)

[TJOI2013]獎學金

題目描述

小張最近發表了一篇論文,有一個神祕人物要給小張學院發獎學金。小張學院有C名學生,要從中挑出N個。這個神祕人物愛好奇特,他希望得到獎學金的同學的成績的中位數儘可能大,但同時,他們的獎學金總額不能超過F。

輸入格式

第一行包含整數N,C,F。N一定是奇數,N≤C

接下來的C行,每一行描述一個學生的成績和獎學金。0≤成績≤2*10^ 9,0≤獎學金≤10^5

輸出格式

滿足條件的最大中位數。如果無解輸出-1。


題目非常簡短;

可以看出,有兩個限制條件,一個是人數,一個是錢數,然後求最大成績;

看起來毫無頭緒,考慮二分思維(暴力思維),枚舉成績當中位數看是否可行;

但是這題沒有單調性,二分顯然不可以,那麼怎麼枚舉可以降低複雜度呢?

可以發現如果以成績排序,那麼該同學一定在n/2+1到c-n/2只間,因爲要保證左邊和右邊最少爲n/2個人;

只要枚舉 i (n/2+1<=i<=c-n/2)就可以,那這題就轉化爲求1 - i 的長度爲n/2的和的最小值,
求 i - c 的長度爲n/2的和的最小值;

優先隊列就可以了;

代碼:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=10100;
const LL mod=1e9+7;
int n,c,f;
LL mx1[N],mx2[N];
LL mmax;
priority_queue<LL>qu;
struct Node{
	int a;
	LL b;
}stu[N];
bool cmp(Node p,Node q){
	return p.a<q.a;
}
int main(){
	scanf("%d%d%d",&n,&c,&f);
	for(int i=1;i<=c;i++) scanf("%d%lld",&stu[i].a,&stu[i].b);
	sort(stu+1,stu+1+c,cmp);
	for(int i=1;i<=n/2;i++){
		mmax+=stu[i].b;
		qu.push(stu[i].b);
	}
	for(int i=n/2+1;i<=c-n/2;i++){
		mx1[i]=mmax;
		if(stu[i].b<qu.top()){
			mmax-=qu.top(),mmax+=stu[i].b;
			qu.pop();
			qu.push(stu[i].b);
		}
	}
	while(!qu.empty()) qu.pop();
	mmax=0;
	for(int i=c;i>=c-n/2+1;i--){
		mmax+=stu[i].b;
		qu.push(stu[i].b);
	}
	for(int i=c-n/2;i>=n/2+1;i--){
		mx2[i]=mmax;
		if(stu[i].b<qu.top()){
			mmax-=qu.top(),mmax+=stu[i].b;
			qu.pop();
			qu.push(stu[i].b);
		}
	}
	for(int i=c-n/2;i>=n/2+1;i--){
		if(mx1[i]+mx2[i]+stu[i].b<=f){
			printf("%d\n",stu[i].a);
			return 0;
		}
	}
	printf("-1\n");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章