优先队列 - Moo University - Financial Aid - POJ - 2010
题意:
首 行 输 入 三 个 整 数 n , m , F 首行输入三个整数n,m,F 首行输入三个整数n,m,F
接 着 输 入 m 行 正 整 数 数 对 ( a i , b i ) 。 接着输入m行正整数数对(a_i,b_i)。 接着输入m行正整数数对(ai,bi)。
从 m 个 数 对 中 挑 选 n 个 , ( a i 1 , b i 1 ) , ( a i 2 , b i 2 ) , . . . , ( a i n , b i n ) , 需 满 足 ∑ k = 1 n b i k ≤ F 。 从m个数对中挑选n个,(a_{i_1},b_{i_1}),(a_{i_2},b_{i_2}),...,(a_{i_n},b_{i_n}),需满足\sum_{k=1}^{n}b_{i_k}≤F。 从m个数对中挑选n个,(ai1,bi1),(ai2,bi2),...,(ain,bin),需满足∑k=1nbik≤F。
在 满 足 以 上 约 束 条 件 的 前 提 下 , 使 得 数 列 a i 1 , a i 2 , . . . , a i n 的 中 位 数 最 大 。 输 出 最 大 的 中 位 数 。 在满足以上约束条件的前提下,使得数列a_{i_1},a_{i_2},...,a_{i_n}的中位数最大。输出最大的中位数。 在满足以上约束条件的前提下,使得数列ai1,ai2,...,ain的中位数最大。输出最大的中位数。
保 证 n 是 奇 数 。 保证n是奇数。 保证n是奇数。
若 无 法 满 足 前 提 条 件 , 输 出 − 1 。 若无法满足前提条件,输出-1。 若无法满足前提条件,输出−1。
Sample Input:
3 5 70
30 25
50 21
20 20
5 18
35 30
Sample Output:
35
数据范围:
1 ≤ n ≤ 19 , 999 , n ≤ m ≤ 100 , 000 , 0 ≤ F ≤ 2 , 000 , 000 , 000 T i m e l i m i t : 1000 m s , M e m o r y l i m i t : 30000 k B 1 ≤ n ≤ 19,999,n ≤m ≤ 100,000,0 ≤ F ≤ 2,000,000,000\\Time \ limit:1000 ms,Memory\ limit:30000 kB 1≤n≤19,999,n≤m≤100,000,0≤F≤2,000,000,000Time limit:1000ms,Memory limit:30000kB
分析:
由 于 n 是 奇 数 , 中 位 数 两 侧 的 数 的 个 数 相 同 , 都 是 ⌊ n 2 ⌋ 个 。 由于n是奇数,中位数两侧的数的个数相同,都是\lfloor\frac{n}{2}\rfloor个。 由于n是奇数,中位数两侧的数的个数相同,都是⌊2n⌋个。
因 此 , 我 们 可 以 将 数 对 ( a i , b i ) 以 a i 为 第 一 关 键 字 排 序 , 然 后 从 大 到 小 开 始 枚 举 中 位 数 , 判 断 是 否 可 行 即 可 。 因此,我们可以将数对(a_i,b_i)以a_i为第一关键字排序,然后从大到小开始枚举中位数,判断是否可行即可。 因此,我们可以将数对(ai,bi)以ai为第一关键字排序,然后从大到小开始枚举中位数,判断是否可行即可。
枚 举 中 位 数 后 a i 后 , 相 当 于 a i 两 侧 分 别 挑 选 ⌊ n 2 ⌋ 个 数 对 。 枚举中位数后a_i后,相当于a_i两侧分别挑选\lfloor\frac{n}{2}\rfloor个数对。 枚举中位数后ai后,相当于ai两侧分别挑选⌊2n⌋个数对。
记 a i 左 侧 挑 选 的 ⌊ n 2 ⌋ 个 数 对 为 ( a l 1 , b l 1 ) , ( a l 2 , b l 2 ) , . . . , ( a l n 2 , b l n 2 ) , 记 S l = ∑ k = 1 ⌊ n 2 ⌋ b l k 记a_i左侧挑选的\lfloor\frac{n}{2}\rfloor个数对为(a_{l_1},b_{l_1}),(a_{l_2},b_{l_2}),...,(a_{l_\frac{n}{2}},b_{l_\frac{n}{2}}),记S_l=\sum_{k=1}^{\lfloor\frac{n}{2}\rfloor}b_{l_k} 记ai左侧挑选的⌊2n⌋个数对为(al1,bl1),(al2,bl2),...,(al2n,bl2n),记Sl=∑k=1⌊2n⌋blk
右 侧 挑 选 的 ⌊ n 2 ⌋ 个 数 对 为 ( a r 1 , b r 1 ) , ( a r 2 , b r 2 ) , . . . , ( a r n 2 , b r n 2 ) , 记 S r = ∑ k = 1 ⌊ n 2 ⌋ b r k 右侧挑选的\lfloor\frac{n}{2}\rfloor个数对为(a_{r_1},b_{r_1}),(a_{r_2},b_{r_2}),...,(a_{r_\frac{n}{2}},b_{r_\frac{n}{2}}),记S_r=\sum_{k=1}^{\lfloor\frac{n}{2}\rfloor}b_{r_k} 右侧挑选的⌊2n⌋个数对为(ar1,br1),(ar2,br2),...,(ar2n,br2n),记Sr=∑k=1⌊2n⌋brk
要 使 得 总 和 不 超 过 F , 至 少 要 满 足 a i 左 右 两 侧 的 S l m i n + S r m i n + b i ≤ F 。 要使得总和不超过F,至少要满足a_i左右两侧的S_{l_{min}}+S_{r_{min}}+b_i≤F。 要使得总和不超过F,至少要满足ai左右两侧的Slmin+Srmin+bi≤F。
故 我 们 预 处 理 数 组 l [ i ] 来 存 储 在 [ 1 , i − 1 ] 挑 选 ⌊ n 2 ⌋ 个 数 对 的 S l m i n , 故我们预处理数组l[i]来存储在[1,i-1]挑选\lfloor\frac{n}{2}\rfloor个数对的S_{l_{min}}, 故我们预处理数组l[i]来存储在[1,i−1]挑选⌊2n⌋个数对的Slmin,
用 r [ i ] 来 存 储 在 [ i + 1 , m ] 挑 选 ⌊ n 2 ⌋ 个 数 对 的 S r m i n 。 用r[i]来存储在[i+1,m]挑选\lfloor\frac{n}{2}\rfloor个数对的S_{r_{min}}。 用r[i]来存储在[i+1,m]挑选⌊2n⌋个数对的Srmin。
问 题 转 化 为 求 某 段 区 间 内 前 ⌊ n 2 ⌋ 小 的 数 的 和 。 问题转化为求某段区间内前\lfloor\frac{n}{2}\rfloor小的数的和。 问题转化为求某段区间内前⌊2n⌋小的数的和。
这 我 们 可 以 通 过 大 根 堆 来 维 护 。 这我们可以通过大根堆来维护。 这我们可以通过大根堆来维护。
最 后 仅 需 判 断 l [ i ] + r [ i ] + b [ i ] ≤ F 是 否 成 立 即 可 。 最后仅需判断l[i]+r[i]+b[i]≤F是否成立即可。 最后仅需判断l[i]+r[i]+b[i]≤F是否成立即可。
代码:
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<queue>
#define P pair<int,int>
#define x first
#define y second
using namespace std;
const int N=1e5+10;
int n,m,F;
P calf[N];
int l[N],r[N];
priority_queue<int> heap;
int main()
{
cin>>n>>m>>F;
for(int i=1;i<=m;i++) scanf("%d%d",&calf[i].x,&calf[i].y);
sort(calf+1,calf+m+1);
memset(l,0x3f,sizeof l);
memset(r,0x3f,sizeof r);
//求l[i]
int sum=0;
for(int i=1;i<=m;i++)
{
if(heap.size()<n/2) {heap.push(calf[i].y);sum+=calf[i].y;}
else
{
l[i]=sum;
if(calf[i].y<heap.top())
{
sum-=heap.top();
heap.pop();
heap.push(calf[i].y);
sum+=calf[i].y;
}
}
}
//求r[i]
heap=priority_queue<int>();
sum=0;
for(int i=m;i;i--)
{
if(heap.size()<n/2) {heap.push(calf[i].y);sum+=calf[i].y;}
else
{
r[i]=sum;
if(calf[i].y<heap.top())
{
sum-=heap.top();
heap.pop();
heap.push(calf[i].y);
sum+=calf[i].y;
}
}
}
bool flag=true;
for(int i=m-n/2;i>n/2;i--)
if(l[i]+calf[i].y+r[i]<=F)
{
flag=false;
cout<<calf[i].x<<endl;
break;
}
if(flag) puts("-1");
return 0;
}