CF I. Olympiad in Programming and Sports

原题链接:Problem - 730I - Codeforces

题意:有 n个学生每人有两种技能,分别是 a,b 表示编程能力和运动能力。你需要将他们分为两个团队分别参加编程比赛和体育比赛,编程团队有 p 人,体育团队有 s 人,一个学生只能参加其中一项。每个团队的力量是其成员在对应领域能力总和,请问如何分配能使得两个团队的实力和最大?

思路:反悔贪心。可以想到肯定是需要让编程能力强的人去编程,运动能力强的人去运动,但是贪心无法保证正确性,例如一个人编程能力和运动能力分别是6 5,另一个人是6 1,那么应该让第一个人去运动,第二个人编程,如果直接贪心是错误的。那么就需要对之前的贪心进行反悔,首先先把编程能力最强的p人放到编程团队,对于运动团队的选人来说,有二种情况,情况一:直接在没选择的人中选择运动能力最强的,情况二:将编程团队中运动能力-编程能力最大的人放入运动团队,然后从没选的人里面取出最强的编程能力放入编程团队。二种情况取最大值就可以了。使用三个堆a,b,c来维护,b维护没选择的人里面运动能力最强的,a维护没选择里面编程能力最强的,c维护编程团队里面运动能力-编程能力最强的。

//冷静,冷静,冷静
//调不出来就重构
//#pragma GCC optimize(2)
//#pragma GCC optimize("O3")
#include<bits/stdc++.h>
#define count2(x) __builtin_popcountll(x)
#define is2(x) __builtin_ffsll(x)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> pii;
const int N=1e6+10,mod=1000000007;
struct node
{
	ll id,num;
}p[N],q[N];
bool gz(node &a,node &b)
{
	return a.num>b.num; 
}
bool gz1(node &a,node &b)
{
	return a.id<b.id;
}
ll ans[N];
struct gz2
{
	bool operator()(const node &a,const node &b)const
	{
		return a.num<b.num;
	}
};
void Jiuyuan()
{
	ll n,x,y;cin>>n>>x>>y;
	for(int i=1;i<=n;i++)
	{
		cin>>p[i].num;p[i].id=i;
	}
	for(int i=1;i<=n;i++)
	{
		cin>>q[i].num;q[i].id=i;
	}
	sort(p+1,p+1+n,gz);
	ll max1=0;
	for(int i=1;i<=x;i++)
	{
		ans[p[i].id]=1;
		max1+=p[i].num;
	}
	sort(p+1,p+1+n,gz1);
	priority_queue<node,vector<node>,gz2> a,b,c;
	for(int i=1;i<=n;i++)
	{
		a.push({i,p[i].num});
		b.push({i,q[i].num});
		c.push({i,q[i].num-p[i].num});
	}
	for(int i=1;i<=y;i++)
	{
		ll now=-1e18,id1,id2,hh=0;
		while(b.size()&&ans[b.top().id])b.pop();
		if(b.size()&&b.top().num>now)
		{
			now=b.top().num;
			hh=1;
			id2=b.top().id;
		}
		while(a.size()&&ans[a.top().id])a.pop();
		while(c.size()&&ans[c.top().id]!=1)c.pop();
		if(a.size()&&c.size())
		{
			ll v=a.top().num+c.top().num;
			if(v>now)
			{
				hh=2;
				id2=c.top().id;
				id1=a.top().id;
				now=v; 
			}
		}
		max1+=now;
		if(hh==1)
		{
			ans[id2]=2;
			b.pop();
		}
		else
		{
			ans[id2]=2;ans[id1]=1;
			a.pop();c.pop();
			c.push({id1,q[id1].num-p[id1].num});
		}
	}
	cout<<max1<<endl;
	for(int i=1;i<=n;i++)if(ans[i]==1)cout<<i<<' ';
	cout<<endl;
	for(int i=1;i<=n;i++)if(ans[i]==2)cout<<i<<' ';
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	ll T=1;
//	cin>>T;
	while(T--)
	{
		Jiuyuan();
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值