【Codeforces 521D】Shop
贪心
题目大意:
给定 $n$ 个数,$m$ 个操作,要求选择不超过 $k$ 个操作并按一定顺序执行。要求使得最后所有数的乘积最大。
求一种操作的方案。
操作有三种:单点赋值,单点加,单点乘。
所有整数非负。
题解:
对某个数,执行操作的顺序显然是赋值、加、乘。
由于我们是要使总乘积最大,所以乘法操作的贡献是一定的,不会因为原数具体是多少而改变。
赋值操作显然最多执行一次,赋值为最大的。
而加法操作,我们显然是从大到小取最优。
考虑把加法操作转化为乘法操作,那么 $a+x$ 可以看做 $a\times \frac{a+x}{a}$。我们把 $\frac{a+x}a$ 看做要乘的数就好了。
考虑赋值操作,可以将其转化为一个加法操作,并和加法一起考虑。
这样做并不会影响顺序,原因:如果赋值操作没被选取则无影响。如果赋值操作被选取,则实际操作时将其放到前面,那么它确实产生了贡献。
然后我们就把所有操作都转化为乘法了。用分数存储每个乘数,排序后取最前面的即可。
时间复杂度 $O(m\log m)$。
Code:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+5;
typedef long long LL;
struct frac{
LL x,y;
inline bool operator<(const frac&rhs)const{return (long double)x/y<(long double)rhs.x/rhs.y;}
};
struct data{
frac f;int id;
inline bool operator<(const data&rhs)const{return f<rhs.f;}
};
vector<pair<int,int> >add[N];
vector<data>p;
struct opts{
int tp,x,val;
}g[N];
int n,m,k,cov[N],a[N];
vector<int>ans[4];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=m;++i){
cin>>g[i].tp>>g[i].x>>g[i].val;
if(g[i].tp==1){
if(!cov[g[i].x]||g[cov[g[i].x]].val<g[i].val)cov[g[i].x]=i;
}else
if(g[i].tp==2){
add[g[i].x].push_back(make_pair(g[i].val,i));
}
else{
p.push_back((data){(frac){g[i].val,1},i});
}
}
for(int i=1;i<=n;++i){
LL x=a[i];
if(cov[i]&&g[cov[i]].val>x)add[i].push_back(make_pair(g[cov[i]].val-x,cov[i]));
sort(add[i].rbegin(),add[i].rend());
for(int j=0;j<add[i].size();++j)
p.push_back((data){(frac){x+add[i][j].first,x},add[i][j].second}),x+=add[i][j].first;
}
sort(p.rbegin(),p.rend());
for(int i=0;i<k&&i<p.size();++i)
ans[g[p[i].id].tp].push_back(p[i].id);
cout<<ans[1].size()+ans[2].size()+ans[3].size()<<'\n';
for(int x=1;x<=3;++x)
for(int i=0;i<ans[x].size();++i)cout<<ans[x][i]<<' ';
return 0;
}