Jzoj5432 三元组

227 篇文章 3 订阅
153 篇文章 0 订阅

有X+Y+Z个三元组(x[i],y[i],z[i]),请你从每个三元组中挑数,并满足以下条件:
1、每个三元组中可以且仅可以选择一个数(即x[i],y[i],z[i]中的一个)
2、选择x[i]的三元组个数恰好为X
3、选择y[i]的三元组个数恰好为Y
4、选择z[i]的三元组个数恰好为Z问选出的数的和最大是多少
问选出的数的和最大是多少

贪心的题全部都是差分方法做的。。。无语了全都不会。。。。

我们先强制所有都选择x,让后三元组就变成了(0,y[i]-x[i],z[i]-x[i])->(y'[i],z'[i])

让后我们将二元组按照y'[i]-z'[i]排序

接下来用两个堆,计算出Ansy[Y],Ansy[Y+1]...Ansy[n]和Ansz

这里Ansy[k]代表在前k个数中,选出Y个y之和最大的和,Ansz类似,只不过是从后面开始计算

那么Max=Σxi+max(Ansy[k]+Ansz[k]){Y<=k<=n-Z}

一个log轻松搞定

#pragma GCC optimize("O2")
#pragma G++ optimize("O2")
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 500010
using namespace std;
struct dt{ int y,z; } s[N];
long long sx=0,sv=0,b[N],c[N]; int X,Y,Z,n;
priority_queue<int,vector<int>,greater<int> > q1,q2;
inline bool operator< (dt a,dt b){ return (a.z-a.y)>(b.z-b.y); }
int main(){
	freopen("triple.in","r",stdin);
	freopen("triple.out","w",stdout); 
	scanf("%d%d%d",&X,&Y,&Z); n+=X+Y+Z;
	for(int x,i=1;i<=n;++i){
		scanf("%d%d%d",&x,&s[i].y,&s[i].z);
		s[i].z-=x; s[i].y-=x; sx+=x;
	}
	sort(s+1,s+1+n);
	for(int i=1;i<=Z;++i) q1.push(s[i].z),b[Z]+=s[i].z;
	for(int i=Z+1;i<=n;++i){
		b[i]=b[i-1]; 
		if(s[i].z>q1.top()){ b[i]+=s[i].z-q1.top(); q1.pop(); q1.push(s[i].z); }
	}
	reverse(s+1,s+1+n);
	for(int i=1;i<=Y;++i) q2.push(s[i].y),c[Y]+=s[i].y;
	for(int i=Y+1;i<=n;++i){
		c[i]=c[i-1];
		if(s[i].y>q2.top()){ c[i]+=s[i].y-q2.top(); q2.pop(); q2.push(s[i].y); }
	}
	for(int i=Z;i<=n-Y;++i) sv=max(sv,b[i]+c[n-i]);
	printf("%lld\n",sv+sx);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值