【JZOJ 5432】【NOIP2017提高A组集训10.28】三元组

Description

有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问选出的数的和最大是多少
问选出的数的和最大是多少

Solution1

考虑可撤销贪心,
显然,这题你可以通过交换两个决策,使答案越来越优,直到最优,
当然,有可能不止是交换两个决策,而是交换三个决策(都是个环)

这个用线段树或堆维护即可,复杂度O(nlog(n))

发现数据=给出的范围比较小,可以用像桶一样的方式维护即可,
复杂度: O(n)

Solution2

假设n3=0,

先全部选第一个,
那么第二个怎么选呢?肯定是选减掉第一个加上第二个最大的那前n2个,
那试着推广一下,

先全部选第一个,
枚举n2选i个(i>=n2),
在选的i个中选(-第二个加第三个)的值前(i-n2)大的,他们就选第三个了,
剩下的第三个就从第一个中选,
这个用对维护就是 O(nlog(n)) 的,

但我们发现,数的范围比较小,用像桶一样的方式维护即可,

复杂度: O(n)

Code

#pragma GCC optimize(2)
#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
#define JS(q,i,j) ((q)?a[q][j]-a[q][i]:-1e9)
#define MX(i,j,q,w) ((JS(q,i,j)>JS(w,i,j))?q:w)
using namespace std;
typedef long long LL;
const int N=500500;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,n1,n2,n3;
LL ans;
int a[N][3];
int b1[3][N],bv[N];
int b[N*4][3][3];
void build(int l,int r,int e)
{
    if(l==r)
    {
        b[e][bv[l]][0]=l;
        b[e][bv[l]][1]=l;
        b[e][bv[l]][2]=l;
        return;
    }
    int t=(l+r)>>1;
    build(l,t,e*2);
    build(t+1,r,e*2+1);
    fo(i,0,2)fo(j,0,2)b[e][i][j]=MX(i,j,b[e*2][i][j],b[e*2+1][i][j]);
}
void change(int l,int r,int e,int l1,int l2,int l3)
{
    if(l==r)
    {
        b[e][bv[l]][0]=0;
        b[e][bv[l]][1]=0;
        b[e][bv[l]][2]=0;
        bv[l]=l2;
        b[e][l2][0]=l;
        b[e][l2][1]=l;
        b[e][l2][2]=l;
        return;
    }
    int t=(l+r)>>1;
    if(l1<=t)change(l,t,e*2,l1,l2,l3);
    else change(t+1,r,e*2+1,l1,l2,l3);
    fo(i,0,2)
    {
        b[e][l2][i]=MX(l2,i,b[e*2][l2][i],b[e*2+1][l2][i]);
        b[e][l3][i]=MX(l3,i,b[e*2][l3][i],b[e*2+1][l3][i]);
    }
}
int main()
{
    freopen("triple.in","r",stdin);
    freopen("triple.out","w",stdout);
    int q,w,e;
    read(n1),read(n2),read(n3);
    n=n1+n2+n3;
    fo(i,1,n)fo(j,0,2)read(a[i][j]);
    q=1;
    fo(i,1,n1)ans+=(LL)a[i][0],b1[0][++q]=i,bv[i]=0;
    q=0;
    fo(i,n1+1,n1+n2)ans+=(LL)a[i][1],b1[1][++q]=i,bv[i]=1;
    q=0;
    fo(i,n1+n2+1,n)ans+=(LL)a[i][2],b1[2][++q]=i,bv[i]=2;
    build(1,n,1);
    while(1)
    {
        int t=-1e9;e=0;
        fo(i,0,2)fo(j,i+1,2)
        {
            int t1=JS(b[1][i][j],i,j)+JS(b[1][j][i],j,i);
            if(t1>t)t=t1,q=i,w=j;
        }
        int t1=JS(b[1][0][1],0,1)+JS(b[1][1][2],1,2)+JS(b[1][2][0],2,0);
        if(t1>t)t=t1,q=1,w=2,e=3;
        t1=JS(b[1][0][2],0,2)+JS(b[1][2][1],2,1)+JS(b[1][1][0],1,0);
        if(t1>t)t=t1,q=1,w=3,e=2;
        t1=JS(b[1][1][0],1,0)+JS(b[1][0][2],0,2)+JS(b[1][2][1],2,1);
        if(t1>t)t=t1,q=2,w=1,e=3;
        t1=JS(b[1][1][2],1,2)+JS(b[1][2][0],2,0)+JS(b[1][0][1],0,1);
        if(t1>t)t=t1,q=2,w=3,e=1;
        t1=JS(b[1][2][0],2,0)+JS(b[1][0][1],0,1)+JS(b[1][1][2],1,2);
        if(t1>t)t=t1,q=3,w=1,e=2;
        t1=JS(b[1][2][1],2,1)+JS(b[1][1][0],1,0)+JS(b[1][0][2],0,2);
        if(t1>t)t=t1,q=3,w=2,e=1;
        if(t>0)
        {
            ans+=(LL)t;
            if(!e)
            {
                change(1,n,1,b[1][q][w],w,q);
                change(1,n,1,b[1][w][q],q,w);
            }else
            {
                q--,w--,e--;
                change(1,n,1,b[1][q][w],w,q);
                change(1,n,1,b[1][w][e],e,w);
                change(1,n,1,b[1][e][q],q,e);
            }
        }else break;
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值