题目大意
给定
X+Y+Z
个三元组
(xi,yi,zi)
,每个三元组只能选
xi,yi,zi
的其中一个并统计入答案,问恰好选X个
xi
,Y个
yi
,Z个
zi
的答案的最大值。
数据范围
对于100%的数据满足,1<=X+Y+Z<=500000,0<=x[i],y[i],z[i]<=500000.
题解
对于
1<=X+Y+Z<=100
显然DP。(设
f[i][j][k]
表示目前选了i个x,j个y,k个z)
100%的做法,有很多种。
对于X=0,按照
yi−zi
来排个序,选择前Y个的
yi
,其余的选
zi
。
X>0
的情况,三元组太复杂了,将它变成二元组吧。
即将所有x选了,三元组变成
(0,yi−xi,zi−xi)
。
然后根据选y还是选z的优越性来做文章。
审一波题目的条件:
①三元组内只能够选一个元素。
②选择元素要考虑全局最优。
③选择每类的元素个数受到了限制。
所以我们设计的算法必须要考虑到每一种情况(即每个元素究竟选x/y/z)。
如果没有条件③,那么直接排序,判断一下这个三元组有没被选择过元素就好了。
考虑将三个限制变为两个限制。所以一开始x全部选,然后三元组变二元组。
接下来再考虑选哪些y,哪些z对答案有利。
所以要确定哪些y-x要被选,哪些z-x要被选.(一种情况讨论不完,所以要枚举分界点讨论哪种情况最优)
Alan的方法:按照y-x从大到小排,枚举分界点i,讨论每种i前面选y-x或z-x的情况。他选择在i之前选y或z而不选x,是因为如果选了x,那么第i位选y没有前面那位选y那么优。如果第i位选择了x,那么第i位选z的情况就会没有被讨论到。
即前i位中选前Y大的y-x,剩下的i-Y个选z,后面前Z-i+Y位z-x也选z。
用数据结构维护很简单,而我比较蒟蒻。我种了3棵线段树,不仅MLE还TLE……
题解方法:
也是枚举一个界点i,i前面的选前Z个z-x,i后面的选前Y个y-x。
(每种情况都要让选的z和y都对答案有利)
不管怎么样,都要选择分界点前/后的前几个元素。而i每往右移,就是加了个z-x减了个y-x,那么i前面第Z位的z-x不会递减,同样的,i后面第Y位的y-x因为前Y位的y-x可能被删掉而不会递增。
所以排序之后用两个指针维护一下就好了。(指针难打?)
先将y-x和z-x还有z-y排好序,然后先选排名前Z的z-y。
用bool[]维护这个元素是否能被选。
总结
①减少制约条件。当制约条件过多的时候,尝试减少一个(些)比如说这题考虑作差,根据y、z与x的差,来尽量使答案变优。
②这道题最主要的难点是选的时候无法一次性满足所有条件,所以考虑分类讨论,每类考虑出一种最优的解,从而最优答案就是每种最优解的最优解。
③程序实现方面:这道题目实现起来也是非常麻烦的(调了我几个小时/(ㄒoㄒ)/~~)困惑我的地方主要是不知道怎么确定前Z个z-x和前y个y-x。
对策:可以排个序,顺便维护是哪个三元组(维护一个原来的位置wz),然后用bool[]维护这个元素是否能被选。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 500010
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
LL d,id;
};note y2[N],z2[N],n2[N];
struct note1{
LL yf,zf;
};note1 a[N];
LL i,j,X,Y,Z,n,ans;
LL sum,temp,temp1;
LL x[N],y[N],z[N];
LL num[N],ranky[N],rankz[N],rank[N];
LL zy,zz,wz,sy,sz,cnt;
bool by[N],bz[N];
LL read(){
LL fh=1,res=0;char ch;
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
return res*fh;
}
bool cmp(note x,note y){return x.d>y.d;}
bool cmpy(note x,note y){return x.d>y.d || (x.d==y.d && rank[x.id]<rank[y.id]);}
bool cmpz(note x,note y){return x.d>y.d || (x.d==y.d && rank[x.id]>rank[y.id]);}
int main(){
X=read(),Y=read(),Z=read();
n=X+Y+Z;
fo(i,1,n)x[i]=read(),y[i]=read(),z[i]=read(),sum+=x[i];
fo(i,1,n){
y2[i].id=z2[i].id=n2[i].id=i;
a[i].yf=y2[i].d=y[i]-x[i];
a[i].zf=z2[i].d=z[i]-x[i];
n2[i].d=z[i]-y[i];
by[i]=1;
}
sort(n2+1,n2+n+1,cmp);
fo(i,1,n)rank[n2[i].id]=i;
sort(y2+1,y2+n+1,cmpy);
sort(z2+1,z2+n+1,cmpz);
fo(i,1,Z)sz+=a[n2[i].id].zf,bz[n2[i].id]=1,by[n2[i].id]=0;
zy=0,zz=n;cnt=0;
while(zy<n){
if(cnt==Y)break;
zy++;
if(by[y2[zy].id]){
cnt++;
sy+=y2[zy].d;
}
}
ans=sum+sy+sz;
fo(i,Z+1,n-Y){
wz=n2[i].id;
bz[wz]=1;
by[wz]=0;
if(a[wz].zf>=z2[zz].d){
sz+=a[wz].zf;
while(!bz[z2[zz].id])zz--;//找到目前第Z位,然后删掉它
sz-=z2[zz].d;
zz--;
}
if(a[wz].yf>=y2[zy].d){
sy-=a[wz].yf;
cnt--;
while(zy<n){
if(cnt==Y)break;
zy++;
if(by[y2[zy].id]){
cnt++;
sy+=y2[zy].d;
}
}
}
ans=ans>sum+sy+sz?ans:sum+sy+sz;
}
printf("%lld",ans);
return 0;
}