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;
}

posted @ 2025-05-10 19:24  XP3301_Pipi  阅读(24)  评论(0)    收藏  举报
Title