注意,答案直接向下取整!!!
被这个玩意儿坑了,好惨啊。。。
关于这道题,我们先来看看m是奇数怎么做。
很明显,我们可以考虑枚举中位数,然后判断其是否可行即可。这样,我们先对所有物品按价值从小到大排序。
那么,如果一个物品可以作为中位数,当前仅当存在一种方案,从这个物品的左边选个物品,再从它右边选
个物品,他们的容积和加上该物品的容积小于等于背包容积v。那么,我们明显只需要贪心去取,每次取最小的几个即可。
那么,我们设表示,我们从i号点左/右取
个物品的最小容积和。
对于这个,我们直接开个优先队列,使得优先队列的元素是当前最小的个元素即可。
那么,枚举i时,我们只需要判断下是否小于等于v,即可。
那么,m为偶数怎么做呢?
我们发现,其实,我们可以把它看做我们从左边的中位数的左边拿个物品,再从右边的中位数的右边拿
个物品,所以,对于这个情况,我们参考m为奇数的时候,设
表示从i的左边/右边拿
个物品的最小容积和。
那么,我们就可以开始枚举了,但是,现在的中位数有两个,我们不可能同时枚举两个,那样复杂度会变成n^2,明显是不行的。那么,我们考虑枚举其中一个,然后另一个贪心来弄。我们考虑枚举右边的中位数,编号设为i,那么,其实我们就是要求一个最大的j,使得j<i,并且
那么,很明显,因为对于每个点j,它的是一个常数,我们只需要找到最大的那个符合条件的j即可,直接上个单调栈,然后在单调栈上二分一下就好了。
(我太菜了,打的单调队列qwq)
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
#define int long long
struct node{
int a,b;
}t[N];
int w[N][2],val[N];
priority_queue<int>sp;
inline bool kkk(node x,node y){
return x.a<y.a;
}
int que[N],l=1,r;
signed main(){
int v,n,m;
scanf("%lld%lld%lld",&v,&n,&m);
//枚举算答案,贪心判可行
for(int i=1;i<=n;++i)scanf("%lld%lld",&t[i].a,&t[i].b);
sort(t+1,t+n+1,kkk);
int s,sum=0;
if(m&1)s=(m-1)/2;
else s=(m-2)/2;
for(int i=1;i<=n;++i){
w[i][0]=sum;
if(sp.size()<s)sp.push(t[i].b),sum+=t[i].b;
else{
if(!s)continue;
if(sp.top()>t[i].b)sum-=(sp.top()-t[i].b),sp.pop(),sp.push(t[i].b);
}
}
while(!sp.empty())sp.pop();
sum=0;
for(int i=n;i;--i){
w[i][1]=sum;
if(sp.size()<s)sp.push(t[i].b),sum+=t[i].b;
else{
if(!s)continue;
if(sp.top()>t[i].b)sum-=(sp.top()-t[i].b),sp.pop(),sp.push(t[i].b);
}
}
if(m&1){
int ans=0;
for(int i=s+1;i<=n-s;++i){
int res=w[i][0]+w[i][1]+t[i].b;
if(res<=v)ans=t[i].a;
}
printf("%lld",ans);
system("pause");
return 0;
}else{
int ans=0;
for(int i=s+1;i<=n-s;++i){//枚举右边的中位数
int now=t[i].b+w[i][1];val[i]=t[i].b+w[i][0];
int L=1,R=(r-l+1),res=0;
while(L<=R){
int mid=(L+R)>>1;
if(now+val[que[l+mid-1]]<=v){
res=que[l+mid-1],L=mid+1;
}else{
R=mid-1;
}
}
if(res)ans=max(ans,t[i].a+t[res].a);
while(l<=r&&val[que[r]]>=val[i])--r;
que[++r]=i;
}
ans/=2;
printf("%lld",ans);
}
system("pause");
return 0;
} 
京公网安备 11010502036488号