CF436E Cardboard Box 做题记录
CF436E Cardboard Box 做题记录
Description
\(n\) 个关卡,对每个关卡,你可以花 \(a_i\) 代价得到一颗星,也可以花 \(b_i\) 代价得到两颗星,也可以不玩。问获得 \(m\) 颗星最少需要多少时间,并构造一组使得用时最短的方案。
\(1\leq n\leq 3\times 10^5,1\leq m\leq 2n,1\leq a_i<b_i\leq 10^9\)。
Solution
首先按照 \(b_i\) 排序。
设最优方案中打了 \(x\) 个两星,\(m-2x\) 个一星。
有个很 naive 的想法,就是这 \(x\) 个两星对应一个前缀。但这显然不对,因为两个一星可能要比一个两星更优。
但我们可以发现,最后一个两星之前一定没有零星,否则我们可以让前面的一个零星变为两星让答案更优。
也就是说,在最优方案中,我们可以找到一个分界点 \(p\),使得 $ p$ 之前只有一星或两星,\(p\) 之后只有零星和两星。
枚举这个 \(p\),我们先让 \(1\) 至 \(p\) 全部变为一星,然后再找出升星代价前 \(m-p\) 小的。这个可以权值线段树上二分。
时间复杂度为 \(O(n\log n)\)。
启示:先排序,再调整;枚举分界点。
int n,m,a[N],b[N];
int c[N<<2],k,f[N],g[N],p[N],op[N];
struct Segtr{
int cnt;
ll sum;
}tr[N<<3];
void Pushup(int p){
tr[p].cnt=tr[p<<1].cnt+tr[p<<1|1].cnt;
tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
}
void Update(int p,int l,int r,int x,int v){
if(l==r){
tr[p].cnt+=v;
tr[p].sum+=v*c[l];
return;
}
int mid=(l+r)>>1;
if(x<=mid) Update(p<<1,l,mid,x,v);
else Update(p<<1|1,mid+1,r,x,v);
Pushup(p);
}
ll AskSum(int p,int l,int r,int x){
if(l==r){
if(x>tr[p].cnt) return LINF;
return 1ll*x*c[l];
}
int mid=(l+r)>>1;
if(tr[p<<1].cnt>=x) return AskSum(p<<1,l,mid,x);
return tr[p<<1].sum+AskSum(p<<1|1,mid+1,r,x-tr[p<<1].cnt);
}
bool Cmp(int x,int y){
return b[x]<b[y];
}
struct Node{
int v,id;
bool operator<(const Node& tmp)const{
return v<tmp.v;
}
}d[N];
signed main(){
read(n),read(m);
for(int i=1;i<=n;i++){
read(a[i]),read(b[i]);
c[++k]=a[i];
c[++k]=b[i]-a[i];
}
sort(c+1,c+k+1);
k=unique(c+1,c+1+k)-c-1;
for(int i=1;i<=n;i++){
f[i]=lower_bound(c+1,c+k+1,a[i])-c;
g[i]=lower_bound(c+1,c+k+1,b[i]-a[i])-c;
Update(1,1,k,f[i],1);
p[i]=i;
}
sort(p+1,p+n+1,Cmp);
ll ans=AskSum(1,1,k,m),sum=0,pos=0;
for(int i=1;i<=min(n,m);i++){
sum+=a[p[i]];
Update(1,1,k,f[p[i]],-1);
Update(1,1,k,g[p[i]],1);
ll res=sum+AskSum(1,1,k,m-i);
if(res<ans) ans=res,pos=i;
}
printf("%lld\n",ans);
for(int i=1;i<=pos;i++) op[p[i]]=1,d[i]={b[p[i]]-a[p[i]],i};
for(int i=pos+1;i<=n;i++) op[p[i]]=0,d[i]={a[p[i]],i};
sort(d+1,d+n+1);
for(int i=1;i<=m-pos;i++){
if(d[i].id<=pos) op[p[d[i].id]]=2;
else op[p[d[i].id]]=1;
}
for(int i=1;i<=n;i++) putchar(op[i]+'0');
puts("");
return 0;
}

浙公网安备 33010602011771号