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

题目

【NOIP2017提高A组集训10.28】三元组
(File IO): input:triple.in output:triple.out
Time Limits: 700 ms Memory Limits: 524288 KB
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问选出的数的和最大是多少
问选出的数的和最大是多少

Input

第一行三个非负整数分别表示X,Y,Z
接下来X+Y+Z行每行三个非负整数描述一个三元组(x[i],y[i],z[i])

Output

一行一个整数表示选出的数的和最大是多少

Sample Input

输入1:
1 2 1
2 4 4
3 2 1
7 6 7
5 2 3
输入2:
3 3 2
16 17 1
2 7 5
2 16 12
17 7 7
13 2 10
12 18 3
16 15 19
5 6 2

Sample Output

输出1:
18
输出2:
110

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

题解

首先我们看一下X=0的10point怎么做(这其实提示了我们正解)
假设我们要取t2个2类点,t3个3类点
不妨先全部取2类点,然后把差值最大的t3个转化为3类点,这样显然是最优的

我们现在要拓展这个贪心
设每一个三元组为(x,y,z)那么我们可以先把它转化为(0,y-x,z-x)(设其为(0,y’,z’)),然后最后答案再加上(t1+t2+t3)*x
那么我们就十分惊喜的发现这样的三元组可以当做二元组做了
我们可以先把y-z从大到小排序,设最后一个取y’的位置为i(其实我的程序是i后面没有取y’),那么我们可以发现i前面是不可能取0的(不然我肯定会在前面取0的位置取y’),那么i的前面就只有y’和z’,后面只有z’和0
也就是说在确定了i之后我们就知道了i前面z’的个数以及i后面z’的个数(设为cc1,cc2)
那么我们可以找到i前面最大的cc1个z’-y’,后面最大的cc2个z’
发现范围比较小,所以可以用一个类似于桶排的方法达到线性的复杂度(带log会被卡常)

贴代码

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
#define ll long long
#define cs 500001

using namespace std;

const int maxn=5e5+5;

struct P{
    ll y,z; 
}a[maxn];
int st1[maxn*2],st2[maxn*2];
ll f[maxn],g[maxn];
ll i,j,k,l,n,m,x,y,z,t1,t2,t3,t,ans,sh,now,tot;
char ch;

int read(){
    int x=0;
    ch=getchar();
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9'){
        x=x*10+ch-48;
        ch=getchar();
    }
    return x;
}
int cmp(P x,P y){
    return x.y>y.y;
}
int main(){
    freopen("triple.in","r",stdin);
    freopen("triple.out","w",stdout);
    t1=read(); t2=read(); t3=read();
    t=t1+t2+t3;
    fo(i,1,t){
        x=read(); y=read(); z=read();
        tot+=x; y-=x; z-=x;
        a[i].y=y; a[i].z=z;
    }
    sort(a+1,a+t+1,cmp);
    fo(i,1,t2){
        st1[a[i].z-a[i].y+cs]++;
        now=max(now,a[i].z-a[i].y+cs);
        f[t2]+=a[i].y;
    }
    sh=st1[now];
    fo(i,t2+1,t-t1){
        f[i]=f[i-1]+a[i].y;
        if (a[i].z-a[i].y+cs>=now) f[i]=f[i]+a[i].z-a[i].y; else{
            st1[a[i].z-a[i].y+cs]++;
            if (sh==0){
                now--;
                while (st1[now]==0) now--;
                sh=st1[now];
            }
            sh--;
            f[i]=f[i]+now-cs;
        }
    }
    now=0;
    fo(i,t-t1+1,t){
        st2[a[i].z+cs]++;
        now=max(now,a[i].z+cs);
    }
    sh=st2[now];
    fo1(i,t-t1,t2+1){
        if (a[i].z+cs>=now) g[i]=g[i+1]+a[i].z; else{
            st2[a[i].z+cs]++;
            if (sh==0){
                now--;
                while (st2[now]==0) now--;
                sh=st2[now];
            }
            sh--;
            g[i]=g[i+1]+now-cs;
        }
    }
    fo(i,t2,t-t1) ans=max(ans,f[i]+g[i+1]);
    ans+=tot;
    printf("%lld",ans);
    return 0;
}

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