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

Data Constraint

对于10%的数据满足,1<=X+Y+Z<=15
对于30%的数据满足,1<=X+Y+Z<=100
对于另外10%的数据满足,X=0
对于另外20%的数据满足,所有三元组中的x[i]=0
对于另外20%的数据满足,1<=X+Y+Z<=100000
对于100%的数据满足,1<=X+Y+Z<=500000,0<=x[i],y[i],z[i]<=500000

Solution

两个数很好做。但变成3个数呢?第4档部分分给了提示。我们将x[i],y[i],z[i]全部减去x[i],然后我们把y[i]-z[i]从大到小排序,枚举分界线,分界线以前的取y[]值的前Y大值,分界线以后的取z[]值的前Z大值。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long 
using namespace std;
const ll maxn=5e5+5,maxn1=1e6+10;
struct code{
    ll x,y,z;
}a[maxn];
ll n,m,p,i,t,j,k,l,x,y,z,ans,mi,b[maxn],sum,bz[maxn1],num,ans1;
bool cmp(code x,code y){
    return x.x>y.x;
}
ll get(){
    char ch=getchar();ll x=0;
    while (ch<48 || ch>57) ch=getchar();
    while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
    return x;
}
int main(){
    freopen("triple.in","r",stdin);freopen("triple.out","w",stdout);
    scanf("%lld%lld%lld",&n,&m,&p);
    for (i=1;i<=n+m+p;i++)
        scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z),ans1+=a[i].x,a[i].y-=a[i].x-maxn,a[i].z-=a[i].x-maxn,a[i].x=a[i].y-a[i].z;
    sort(a+1,a+n+m+p+1,cmp);mi=1e9;
    for (i=1;i<=n+m+p;i++){
        b[i]=b[i-1];
        if (num<m) bz[a[i].y]++,mi=min(mi,a[i].y),b[i]+=a[i].y,num++;
        else if (mi<a[i].y){
            bz[mi]--;bz[a[i].y]++;b[i]+=a[i].y-mi;
            while (!bz[mi]) mi++;
        }
    }
    memset(bz,0,sizeof(bz));mi=1e9;num=0;sum=0;
    for (i=n+m+p;i>=1;i--){
        if (num<p) bz[a[i].z]++,mi=min(mi,a[i].z),sum+=a[i].z,num++;
        else if (mi<a[i].z){
            bz[mi]--;bz[a[i].z]++;sum+=a[i].z-mi;
            while (!bz[mi]) mi++;
        }
        if (i>m && num==p) ans=max(ans,b[i-1]+sum);
    }
    ans+=ans1-maxn*(m+p);
    printf("%lld\n",ans);
}
发布了257 篇原创文章 · 获赞 451 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/crybymyself/article/details/78388567