CF521D Shop

题意

(n)个数,有(m)个操作,形如:

  1. (x_i)改成(val_i)
  2. (x_i)加上(val_i)
  3. (x_i)乘上(val_i)

现在你可以从中选择最多(k)个操作(不能重复选),并按一定顺序执行,使得(prod_{i=1}^{n}x_i)
第一行输出选择的操作个数,第二行按执行顺序输出方案。

思路

先考虑,一定先进行赋值,再加,再乘。

如果只有乘法,对答案的贡献都只与乘的数相关。所以我们可以直接按操作的值排序,贪心即可。

接着对于赋值,显然对于一个数只会进行一次,一定赋值为最大的(如果小于原数,不如不做)。那么其实就可以看成是加法了(但一定要在第一个加)

现在的问题是加法如何转化为乘法。对于同一个数,加法肯定是从大到小按先后执行的,按加数大小排序,也就是说,如果进行了操作(i),那么(1,2,...n-1)都一定进行了。因此就可以将加法转化为乘法,设(S_i=a_1+...a_i),考虑第(i+1)个操作,即把(S_i)变成了(S_{i+1}),也就是乘上了(frac{S_{i+1}}{S_i}),这样子就可以都变成乘法贪心了。

但是我突然产生了一个疑问,赋值变为加(简称赋值加)后能跟所有加法一起处理吗?分类思考:

  • 若赋值加没有被加入,答案显然是不受影响的;
  • 若赋值加被加入,把所有乘法乘起来后,可以约分,发现答案就是(S_x),不受顺序影响,所以贪心是正确的

在输出时只要注意按赋值、加、乘、输出即可。

#include <bits/stdc++.h>
const int N=100005;
int n,m,k,a[N],op,p,x,cnt1,cnt2,t[N];
struct note{
	int p,x,id;
}b[N],c[N];
struct node{
	int p,id;
	double x;
}d[N];
bool cmp(note x,note y){
	return x.p==y.p?x.x>y.x:x.p<y.p;
}
bool cmp2(node x,node y){
	return x.x>y.x;
}
bool cmp3(node x,node y){
	return t[x.id]<t[y.id];
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=m;i++){
		scanf("%d%d%d",&op,&p,&x);
		if (op==1 && x>b[p].x && x>a[p]) b[p]={p,x,i},t[i]=1;
		else if (op==2) c[++cnt1]={p,x,i},t[i]=2;
		else if (op==3 && x>1) d[++cnt2]={p,i,x},t[i]=3;
	} 
	for (int i=1;i<=n;i++) if (b[i].x>0) c[++cnt1]={i,b[i].x-a[i],b[i].id};
	std::sort(c+1,c+cnt1+1,cmp);
	long long sum=0;
	for (int i=1;i<=cnt1;i++){
		int p=c[i].p,x=c[i].x;
		if (p!=c[i-1].p)
			sum=a[p];
		d[++cnt2]={p,c[i].id,(sum+x)*1.0/sum};
		sum+=x;
	}
	std::sort(d+1,d+cnt2+1,cmp2);
	k=std::min(k,cnt2);
	printf("%d
",k);
	std::sort(d+1,d+k+1,cmp3);
	for(int i=1;i<=k;i++) printf("%d ",d[i].id);
}
* 生而自由 爱而无畏 *
原文地址:https://www.cnblogs.com/flyfeather6/p/11752200.html