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

Solution

在X=0的时候有一个很显然的做法就是把y-z排个序,然后前面Y个给Y,后面的给Z,这样可以让在Y贡献大的到Y去,在Z贡献大的到Z去。
然后我们来考虑一下X不为0的情况。
我们可以考虑把它转化为第一种情况,我们对于每个y和z都减去x,那么前面的x位就变成0了,那么就变成选y,选z和不选(选x)的情况了,最后把x都加上就好了。
那么这样很显然也是把y-z排个序,但是在前面有不选的情况,所以我们就肯定是选前面Y个y值最大的更优,其他的都留给x,Z的处理也同理,然后这个东西我们可以打个桶来维护。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=5e5+7,mxx=5e5;
int i,j,k,l,t,n,m,X,Y,Z,mi,c[maxn*2];
ll ans,he,f[maxn],g[maxn];
struct node{
    int x,y,z;
}a[maxn];
bool cmp(node x,node y){return x.y-x.z>y.y-y.z;}
int main(){
    freopen("triple.in","r",stdin);
    freopen("triple.out","w",stdout);
    scanf("%d%d%d",&X,&Y,&Z);n=X+Y+Z;
    fo(i,1,n)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),he+=a[i].x,a[i].y-=a[i].x,a[i].z-=a[i].x;
    sort(a+1,a+1+n,cmp);
    mi=0x7fffffff;fo(i,1,Y)mi=min(mi,a[i].y),f[Y]+=a[i].y,c[a[i].y+mxx]++;
    fo(i,Y+1,n){
        if(mi>=a[i].y)f[i]=f[i-1];
        else{
            c[a[i].y+mxx]++;f[i]=f[i-1]+a[i].y-mi;
            c[mi+mxx]--;while(!c[mi+mxx])mi++;
        }
    }
    memset(c,0,sizeof(c));
    mi=0x7fffffff;fo(i,n-Z+1,n)mi=min(mi,a[i].z),g[n-Z+1]+=a[i].z,c[a[i].z+mxx]++;
    fod(i,n-Z,1){
        if(mi>=a[i].z)g[i]=g[i+1];
        else{
            c[a[i].z+mxx]++;g[i]=g[i+1]+a[i].z-mi;
            c[mi+mxx]--;while(!c[mi+mxx])mi++;
        }
    }
    fo(i,Y,n-Z+1)ans=max(ans,f[i]+g[i+1]+he);
    printf("%lld\n",ans);
}

你可能感兴趣的:(noip,贪心,暴搜)