[2017纪中10-28]三元组 贪心+排序/优先队列

题面
考虑 X=0 的时候怎么做,显然是先强制所有都选择 y[i],然后按照 z[i]-y[i]从大到小排序,然后选择前 Z 大的更改为 z[i]。
那么当 X>0 的时候,我们先强制所有都选择 x[i],然后将 z[i]-y[i]从大到小排序,枚举一个 z[i]-y[i]的分界值,选择 z[i]都在分界值以前,选择 y[i]的都在分界值以后,那么可以发现,由于前面已经强制都选择 x[i],那么现在选择 z[i]的就是分界值之前的三元组里 z[i]-x[i]最大的 Z 个,分界值后的同理。
枚举分界点的时候相当于每次加入一个 z[i]-x[i]以及删去一个 y[i]-x[i]。可以用优先队列正着反着做两遍维护加入和删去最小值并保持其中元素个数不变。O(nlogn)。
当然还有O(n)做法,首先由于数值的范围都是[0,500000],那么我们可以桶排,注意到不断增加而不删除就会导致第 Z大的值是不递减的,不断删除同理,所以可以先排一次序,然后用两个指针扫一
下就好了(并不会)。
nlogn代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
using namespace std;
int n,nx,ny,nz;
ll cy[500010],cz[500010],sumx,sumy,sumz;
priority_queue <int ,vector<int>, greater<int> > Q,H;
struct node
{
    int x,y,z;
}a[500010];
int read()
{
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x;
}
bool cmp(node a,node b)
{
    return a.y-a.z>b.y-b.z;
}
int main()
{
    nx=read();ny=read();nz=read();
    n=nx+ny+nz;
    for(int i=1;i<=n;i++)
        a[i].x=read(),a[i].y=read(),a[i].z=read(),sumx+=a[i].x;
    sort(a+1,a+n+1,cmp);    
    for(int i=1;i<=ny;i++)
        sumy+=a[i].y-a[i].x,Q.push(a[i].y-a[i].x);
    cy[ny]=sumy;
    for(int i=ny+1;i<=nx+ny;i++)
    {
        if(a[i].y-a[i].x>Q.top())
        {
            sumy+=a[i].y-a[i].x-Q.top();
            Q.pop();
            Q.push(a[i].y-a[i].x);
        }
        cy[i]=sumy;
    }
    for(int i=n;i>ny+nx;i--)
        sumz+=a[i].z-a[i].x,H.push(a[i].z-a[i].x);
    cz[ny+nx+1]=sumz;
    for(int i=ny+nx;i>ny;i--)
    {
        if(a[i].z-a[i].x>H.top())
        {
            sumz+=a[i].z-a[i].x-H.top();
            H.pop();
            H.push(a[i].z-a[i].x);
        }
        cz[i]=sumz;
    }
    ll mx=0;
    for(int i=ny;i<=nx+ny;i++)
        mx=max(mx,cy[i]+cz[i+1]);
    printf("%lld\n",mx+sumx);   
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值