背包 优先队列+二分
背包
https://ac.nowcoder.com/acm/problem/17315
题意:找出m个不超过V的最大权值的中位数;
题解:中位数的找法要区别奇偶,奇数直接就是中间,偶数就是中间两边数相加>>1,首先我们要知道如何处理这个问题,先找M>>1,就是先找中位数左右两边的数字,由于我们要找最大的权值,所以我们按照权值升序,在排大小,那么我们是不是就要尽量使两边的大小尽量小,然后,中位数权值尽量大,因为大小是没有顺序的,只是权值有序,所以我们可以扫一次m/2前面前缀,m/2后面后缀,然后这样子是我们在枚举中位数的时候就可以直接枚举终点,想办法把左右两边的大小尽量最小就是,因为权值必然是左边小右边大,所以中间只要满足总大小<V那么就可以随便搞,然后就是奇数直接就可以枚举终点,但是偶数要麻烦一点,我们先找M/2-1,左边的,然后找M/2右边的,必然左边最后一个点是我们需要枚举的,但是仔细观察,我们权值是有序的,是不是可以利用权值有序,使用二分来找右边最合适的呢,答案是肯定的,然后就是注意二分的区间,还有一件事,为什么我的for里面是i是m开始的,然后又要N-M呢,是因为,我前面一半选着M,后面的也选择了m个,所有只可以在M-m-n的区间选择
#include <bits/stdc++.h>
#define fio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define debug(x) cout << #x << ": " << x << endl;
#define debug1(x) cout<<x<<endl;
#define ll long long
#define ull unsigned long long
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#define mse(a,b) memset(a,b,sizeof a);
#define fro for
#define it int
using namespace std;
const int maxx=1e6+100;
const int mod=1e9+7;
ll gcd(ll a,ll b)
{
while(b^=a^=b^=a%=b);
return a;
}
struct node
{
int a,b;
bool operator <(const node d)const
{
if(a==d.a) return b<d.b;
return a<d.a;
}
};
node ans[maxx];
ll sum1[maxx],sum2[maxx];
//ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; } ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
//inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
int main()
{
int v,m,n;
cin>>v>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>ans[i].a>>ans[i].b;
}
sort(ans+1,ans+1+n);
int x=m&1;m>>=1;
priority_queue<ll>q;
for(int i=1;i<=n;i++)
{
q.push(ans[i].b),sum1[i]=sum1[i-1]+ans[i].b;
if(q.size()>m-1+x) sum1[i]-=q.top(),q.pop();
}
while(q.size()) q.pop();
for(int i=n;i;i--)
{
q.push(ans[i].b),sum2[i]=sum2[i+1]+ans[i].b;
if(q.size()>m) sum2[i]-=q.top(),q.pop();
}
int dou=-0x3f3f;
if(x)
{
for(int i=m+1;i<=n-m;i++)
{
if(sum1[i-1]+sum2[i+1]+ans[i].b<=v) dou=max(dou,ans[i].a);
}
cout<<dou<<endl;
}
else
{
fro(int i=m;i<=n-m;i++)
{
int l =i,r=n-m+1;
while(l<=r)
{
int mid=l+r>>1;
if(sum1[i-1]+ans[i].b+sum2[mid]<=v) l=mid+1;
else
r=mid-1;
}
if(r>i) dou=max(dou,ans[i].a+ans[r].a);
}
cout<<dou/2<<endl;
}
return 0;
}
腾讯公司福利 1143人发布