Moo University - Financial Aid [POJ2010] [堆]
题意:
在C头牛里选N头牛,每头牛需要花掉一定经费ai才能得到一定得bi分,在不超过经费F的情况下,使得N头牛的得分中位数最大。(1 <= N <= 19,999,奇数) (N <= C <= 100,000)(0 <= F <= 2,000,000,000)
输入:(N,C,F;ai,bi)
3 5 70
30 25
50 21
20 20
5 18
35 30
输出: 35
分析:
最朴素的办法:按分数从小到大排列,枚举原点,从右到左扫,区间为[(m+1)/2,n-(m+1)/2],计算左边最小和和右边最小和,判断是否是满足条件,满足即输出。
显然这种n2的办法是不行的。
我们要减复杂度,就要在枚举原点的同时维护左右两边最小和,如何维护?
方法一(较复杂,但是是一个解决问题的想法):
针对右边,我们的需求是:不断加入值,求前k小的和。我们建立一个大根堆,并在开始的时候记录一个sumR,每加入一个点,如果小于大根堆顶,便更新sumR,弹出堆顶,压入新点。
针对左边,我们的需求是:不断删去值,求前k小的和。这个方法就困难在这里,我们在区间[1,(2n-m-1)/2]新建立一个数组new,按花销从小到大排序,同样时原位置小的在前,同时记录该点是否要去除(usd)。什么意思呢?我们最开始在原数组统计的和sumL=sum(1->(m-1)/2),并在(m-1)/2处设立一个指针p。在原数组删去一个数的时候,如果在new对应的位置在p右边则不用管,标记usd=1。如果在p位置或p位置的左边,就在sumL删除这个数,p向右移至第一个usd没被标记的地方,sumL加入这个新数。
方法二:
我们发现不断删去值,求前k小的和要比不断加入值,求前k小的和更加困难,那我们就提前从左向右扫一边,那么针对左边,就是和右边一样的需求了(不断加入值,求前k小的和)。
这道题还是费了我一点脑子的...
代码:
1 #include<set> 2 #include<map> 3 #include<queue> 4 #include<stack> 5 #include<cmath> 6 #include<cstdio> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 #define RG register int 11 #define rep(i,a,b) for(RG i=a;i<=b;++i) 12 #define per(i,a,b) for(RG i=a;i>=b;--i) 13 #define ll long long 14 #define inf (1<<29) 15 #define maxn 100005 16 using namespace std; 17 int n,m,F,L,R,rt; 18 int sL,sR; 19 int po[maxn]; 20 struct D{ 21 int cost,sco; 22 inline int operator < (const D &tmp)const{ 23 return sco<tmp.sco; 24 } 25 }a[maxn]; 26 struct Dat{ 27 int val,id,usd; 28 inline int operator < (const Dat &tmp)const{ 29 return (val==tmp.val)?id<tmp.id:val<tmp.val; 30 } 31 }b[maxn]; 32 priority_queue<int> que; 33 inline int read() 34 { 35 int x=0,f=1;char c=getchar(); 36 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 37 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 38 return x*f; 39 } 40 41 void work() 42 { 43 L=(m+1)/2,R=n-(m-1)/2; 44 45 int end=(2*n-m+3)/2; 46 per(i,n,end) que.push(a[i].cost),sR+=a[i].cost; 47 48 49 end=(2*n-m-1)/2,rt=(m-1)/2; 50 rep(i,1,end) b[i].val=a[i].cost,b[i].id=i; 51 sort(b+1,b+1+end); 52 rep(i,1,end) po[b[i].id]=i; 53 rep(i,1,rt) sL+=b[i].val; 54 55 end=(2*n-m+3)/2; 56 per(O,R,L) 57 { 58 if(sL+sR+a[O].cost<=F) {printf("%d\n",a[O].sco);return;} 59 60 if(a[O].cost<que.top()) sR-=que.top()-a[O].cost,que.pop(),que.push(a[O].cost); 61 62 b[po[O-1]].usd=1; 63 if(po[O-1]<=rt) 64 { 65 while(b[rt].usd&&rt<=end) rt++; 66 if(rt==(2*n-m+5)/2) {puts("-1");return;} 67 sL+=b[rt].val-a[O-1].cost; 68 } 69 } 70 puts("-1");return; 71 } 72 73 void dif() 74 { 75 per(i,n,1) 76 if(a[i].cost<=F){printf("%d\n",a[i].sco);return;} 77 exit(0); 78 79 } 80 81 int main() 82 { 83 m=read(),n=read(),F=read(); 84 rep(i,1,n) a[i].sco=read(),a[i].cost=read(); 85 sort(a+1,a+1+n); 86 L=(m+1)/2,R=n-(m-1)/2; 87 if(m==1) dif(); 88 else work(); 89 return 0; 90 }