題面
題意
給出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]);
}