专栏文章

题解:P10747 [SEERC 2020] Neo-Robin Hood

P10747题解参与者 1已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@min3nmrg
此快照首次捕获于
2025/12/01 20:02
3 个月前
此快照最后确认于
2025/12/01 20:02
3 个月前
查看原文
这题在 XCPC 模拟赛中出了,然后赛时思路全对,结果代码炸了调不出来,急眼了。
首先看到这是一个需要你制定顺序的题目,考虑微扰贪心,对于只有两个人的情况,我应该偷谁还谁,解除仇恨在本文中称为还钱。
对于两个人,其偷钱和还钱的钱分别为 p1,m1,p2,m2p_1,m_1,p_2,m_2,如果我先偷 11 比先偷 22 更优,那么一定要满足 p1m2p2m1p_1-m_2 \ge p_2-m_1 移项得 p1+m1p2+m2p_1+m_1\ge p_2+m_2,这就是我们的排序方法。
然后考虑如何计算答案,注意到答案显然是有单调性的,所以我们考虑二分答案,我们排完序之后,偷前面的比偷后面的优,所以我们考虑枚举一个断点,使得断点前面的选出 kk 个得钱最多的来偷,断点后面的选出 kk 个还钱最少的还,这可以使用 priority_queue 来维护,最后判断就做完了。
CPP
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define int long long
using namespace std;
constexpr int N=1e5+10;
int n,pre[N],suf[N];
struct node{int m,p;}a[N];
inline int reads(){
	int c=getchar(),x=0,f=1;
	while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
	return x*f;
}bool check(int k){
	priority_queue<int> q2;
	priority_queue<int,vector<int>,greater<int> >q1;
	memset(pre,0,sizeof(pre));
	memset(suf,0x3f,sizeof(suf));
	int cnt=0;
	for(int i=1;i<=n;i++){
		q1.push(a[i].m),cnt+=a[i].m;
		if(q1.size()>k) cnt-=q1.top(),q1.pop();
		pre[i]=cnt;
	}cnt=0;for(int i=n;i>=1;i--){
		q2.push(a[i].p),cnt+=a[i].p;
		if(q2.size()>k) cnt-=q2.top(),q2.pop();
		suf[i]=cnt;
	}
//	cout<<"-----------------------\n";
//	cout<<k<<"\n";
//	for(int i=1;i<=n;i++) cout<<pre[i]<<" ";
//	puts("");
//	for(int i=1;i<=n;i++) cout<<suf[i]<<" ";
//	puts("");
	for(int i=k;i+k<=n;i++){
		if(pre[i]>=suf[i+1]) return 1;
	}return 0;
}
signed main(){
	n=reads();
	for(int i=1;i<=n;i++) a[i].m=reads();
	for(int i=1;i<=n;i++) a[i].p=reads();
	sort(a+1,a+n+1,[](node a,node b){return a.m+a.p>b.m+b.p;});
	int L=0,R=n/2,ans=0;
	while(L<=R){
		int mid=(L+R)>>1;
		if(check(mid)) ans=mid,L=mid+1;
		else R=mid-1;
	}printf("%lld\n",ans);
	return 0;
}
/*
5
2 3 4 5 6
1 2 3 4 5
*/

评论

1 条评论,欢迎与作者交流。

正在加载评论...