题面
题意
给出n个数,有m种操作,操作分为三类:
1.将第i个数改成某个数
2.将第i个数加上某个数
3.将第i个数乘上某个数
每种操作只能做一次,从中选择至多k个,求操作后的数的乘积的最大值。
做法
贪心,首先操作顺序肯定是:修改(可以看作加,且至多一次),加,乘。
难点在于要加多少个数在开始乘,正确处理方式是将加转化为乘,a+b就相当于a*(a+b/a),而加法的顺序是确定的(从大到小),所以可以将所有加法转化为乘一个分数,然后将分数排序即可。
最后注意要根据操作排序。
因为加法转化为分数可能会爆long long,建议将分数-1来存储。
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first
#define se second
#define N 100100
using namespace std;
ll n,m,k,num[N],cz[N];
P chg[N];
inline ll gcd(ll u,ll v)
{
for(;u&&v&&u!=v;)
{
swap(u,v);
u%=v;
}
return max(u,v);
}
struct Fs
{
ll fz,fm,id;
void yf()
{
ll g=gcd(fz,fm);
fz/=g;
fm/=g;
}
bool operator < (const Fs &u) const
{
return fz*u.fm<fm*u.fz;
}
}tmp;
vector<P>add[N];
vector<ll>ans;
priority_queue<Fs>pq;
inline bool cmp(P u,P v){return u>v;}
inline bool cmp2(ll u,ll v){return cz[u]<cz[v];}
int main()
{
ll i,j,o,p,q;
cin>>n>>m>>k;
for(i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
}
for(i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&o,&p,&q);
cz[i]=o;
if(o==1)
{
chg[p]=max(chg[p],mp(q-num[p],i));
}
else if(o==2)
{
add[p].push_back(mp(q,i));
}
else
{
tmp.fz=q-1;
tmp.fm=1;
tmp.id=i;
pq.push(tmp);
}
}
for(i=1;i<=n;i++)
{
if(chg[i].fi) add[i].push_back(chg[i]);
sort(add[i].begin(),add[i].end(),cmp);
o=num[i];
for(j=0;j<add[i].size();j++)
{
tmp.fz=add[i][j].fi;
tmp.fm=o;
tmp.id=add[i][j].se;
tmp.yf();
pq.push(tmp);
o+=add[i][j].fi;
}
}
for(i=1;i<=k&&!pq.empty();i++)
{
ans.push_back(pq.top().id);
pq.pop();
}
cout<<ans.size()<<endl;
sort(ans.begin(),ans.end(),cmp2);
for(i=0;i<ans.size();i++) printf("%lld ",ans[i]);
}