CF521D Shop
题意
\(n\)个数,有\(m\)个操作,形如:
- 将\(x_i\)改成\(val_i\)
- 将\(x_i\)加上\(val_i\)
- 将\(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\n",k);
std::sort(d+1,d+k+1,cmp3);
for(int i=1;i<=k;i++) printf("%d ",d[i].id);
}
* 生而自由 爱而无畏 *