题目描述
在小Z的家乡,有机房一条街,街上有很多机房。每个机房里都有一万个人在切题。小Z刚刷完CodeChef,准备出来逛逛。
机房一条街有 n 个机房,第 i 个机房的坐标为 xi ,小Z的家坐标为 0。小Z在街上移动的速度为1,即从 x1 到 x2 所耗费的时间为 |x1 − x2|。每个机房的学生数量不同,ACM 题目水平也良莠不齐。小Z到达第 i 个机房后,可以花 ti 的时间想题,然后瞬间 AK;当然,也可以过机房而不入。
小Z现在只有 m 个单位时间,之后他就该赶着去打 Codeforces 了。现在他想知道自己最多能在多少个机房 AK,希望你帮帮他。
输入输出格式
输入格式:
第一行包含两个整数 n,m。
接下来 n 行,每行包含两个整数 xi,ti 。
输出格式:
第一行包含一个整数,表示小Z最多能 AK 的机房数量。
输入输出样例
输入样例#1:
2 10
1 100
5 5
输出样例#1:
1
说明
【数据规模】
对于 30% 的数据,n ≤ 20。
对于 60% 的数据,n ≤ 1000。
对于 100% 的数据,1 ≤ n ≤ 10^5,0 ≤ m,xi ≤ 10^18,0 ≤ ti ≤ 10^9。
设二元组f[i]表示当前走到了第i个机房时,剩余多少时间,最多能ak多少题,STA表示选择ak的机房的集合(包括第i个),定义二元组运算,状态转移方程为:
表示如果时间本身就够,那么X可为空集,|x|=0,ak的题就加1,如果时间不够,那就需要在已选择ak的机房中去掉一部分来腾出时间,ak的题的数量就加上(1-去掉的机房数),在二元组运算过程中,剩余时间要保证>=0,最后答案为f[i]在i取1~n中的最大值,可是每次转移的代价太大,因此考虑优化。
由于每个机房都只能ak一次,所以考虑贪心,每次去掉耗时最大的机房,直到时间不超,这样可以用堆来维护选择ak的机房集合。
因为每个机房只会进堆一次出堆一次,所以时间复杂度为O(nlogn)。
#include<iostream>
#include<algorithm>
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MAXN=100005;
struct Bar{
long long x;
int t;
bool operator < (const Bar& X)const{
return x<X.x;
}
}a[MAXN];
int n,ans=0,sz;
long long m,sum,res;
long long heap[MAXN];
inline void pushup(int p) //堆的基本操作
{
int fa=p>>1,a=heap[p];
while(fa&&a>heap[fa]){
heap[p]=heap[fa];
p=fa;
fa>>=1;
}
heap[p]=a;
}
inline void pushdown(int p)
{
int son=p<<1,a=heap[p];
while(son<=sz){
if(son<sz&&heap[son+1]>heap[son]) son++;
if(a>=heap[son]) break;
heap[p]=heap[son];
p=son;
son<<=1;
}
heap[p]=a;
}
inline void insert(int a)
{
heap[++sz]=a;
pushup(sz);
}
inline void Pop()
{
heap[1]=heap[sz--];
pushdown(1);
}
int main()
{
ios::sync_with_stdio(false); //关闭流同步
int i,j;
cin>>n>>m;
f(i,1,n){
cin>>a[i].x>>a[i].t;
}
sort(a+1,a+1+n);
f(i,1,n){
res=m-a[i].x; //能分配给做题的时间
if(res<0) break;
sum+=a[i].t; //做题的总时间
insert(a[i].t); //将要做的题加入堆中
if(res-sum>=0){ //如果能分配的时间足够
ans=max(ans,sz);
}
else{
while(res-sum<0){ //根据贪心,每次把耗时最大的去掉,知道时间够
sum-=heap[1];
Pop();
}
ans=max(ans,sz); //更新答案
}
}
cout<<ans<<endl;
return 0;
}