CF521D
有 k 个正整数 a1…k。
有 n 个操作,每个操作给定正整数 b,有三种可能:将 ai 赋值为 b,将 ai 加上 b,将 ai 乘以 b。
你可以从 n 个操作中选择最多 m 个操作,并按照一定顺序执行。
你的目标是最大化这些 ai 的乘积。
k,n ≤ 1e5。
输出操作个数和操作顺序
=============================================================
对任意一个数的乘法操作都可以直接对整体做贡献,所以将赋值和加法转化为乘法进行比较每个操作对答案的贡献
其它两个操作用贪心考虑,对于同一个数,最多使用一次赋值操作,且该操作需满足赋值后大于原值;而对于加法来说,在尽量少的次数中尽量保证加了尽量大的数。
将赋值转化为加法运算,再将所有加法运算进行比较,同一个数加得多的先操作
然后把加法转化为乘法操作,(原理 :a + b = a * ((a + b) / a))。
最后的操作序列就全是乘法操作了,只需取前 m 次操作即可
code
typedef pair <int,int> PII;
typedef pair <long double,int> PLI
PII as[N];
vector <PII> add[N];
vector <PLI> mul;
bool cmp(PII a,PII b){return t[a.second] < t[b.second];}
int main()
{
cin >> n >> k >> m;
for(int i = 1;i <= n;i ++) cin >> a[i];
for(int i = 1;i <= k;i ++)
{
int x,y,z;
cin >> x >> y >> z;
t[i] = x;//保存操作顺序,最后先输出 操作1 , 2 , 3;
if(x == 1) as[y] = max(as[y],{z,i});
if(x == 2) add[y].push_back({z,i});
if(x == 3) mul.push_back({z,i});
}
for(int i = 1;i <= n;i ++)
if(as[i].first > a[i])
add[i].push_back({as[i].first - a[i],as[i].second});//将所有赋值操作转化为加法操作
for(int i = 1;i <= n;i ++)
{
sort(add[i].begin(),add[i].end());
reverse(add[i].begin(),add[i].end());
//对于加法来说,从大数开始加更优
long long v = a[i];
for(int j = 0;j < add[i].size();j ++)
{
PII p = add[i][j];
mul.push_back({1.0L * (v + p.first) / v,p.second});
v += p.first;//要记录上一次加完后值变为多少,这样才能转化为乘法
}
}
sort(mul.begin(),mul.end());
reverse(mul.begin(),mul.end());
int x = min(m,(int)mul.size());
sort(mul.begin(),mul.begin() + x,cmp);//保证赋值和加法操作在先
cout << x << endl;
for(int i = 0;i < x;i ++)
cout << mul[i].second << ' ';
return 0;
}
这里贴出思路和代码来源