【题解】CF521D Shop
给定一个大小为 \(n\) 的序列和 \(m\) 个操作,每个操作是赋值 \(a_i = x\) 或 \(a_i = a_i + x\) 或 \(a_i = a_i \times x\),现在选出 \(k\) 个操作按任意顺序进行,使得最后 \(\prod a_i\) 最大。
首先由于赋值操作可以覆盖前面的操作,所以每个位置的赋值操作最多进行一次,且一定是这个位置的第一次操作。
由于 \(k(a_i + x) > ka_i + x\),所以乘法操作一定在最后进行。并且由于我们求的是 \(\prod a_i\),所以乘法操作可以看成直接对答案进行操作,我们只用排序后选最大的几个就行。
考虑加法操作,想办法让它变成和乘法操作一样。我们肯定按 \(x\) 从大到小排,优先选择大的。那么一次加法操作等价于乘法操作 \(\times \dfrac{s + x}{s}\),如果两个加法操作 \(x\ge y\),那么对应的两个乘法操作 \(\dfrac{s + x}{s} \ge \dfrac{s + x + y}{s + x}\),所以直接变成乘法操作,顺序不会改变,正确性可以保证。
最后还有赋值操作,由于赋值操作只有一次,可以直接把它看成一次 \(x- a_i\) 的加法即可。时间复杂度 \(\mathcal{O}((n + m + k)\log)\)。
#define N 100005
int n, m, k, a[N], b[N], p[N], o[N];
vector<Pr>u[N];
struct node{
int x, y; // x / y;
bool operator<(const node o)const{return x * (__int128)o.y > o.x * (__int128)y;}
};
vector<pair<node, int> >c;
vector<int>ed;
signed main() {
read(n, m, k);
rp(i, n)read(a[i]);
rp(i, m){
int op, x, y;
read(op, x, y);
o[i] = op;
if(1 == op){
if(y > b[x])b[x] = y, p[x] = i;
}
else if(2 == op)u[x].pb(mp(y, i));
else c.pb(mp(node{y, 1}, i));
}
rp(i, n){
if(b[i] > a[i])u[i].pb(mp(b[i] - a[i], p[i]));
if(!u[i].empty()){
sort(u[i].begin(), u[i].end());
reverse(u[i].begin(), u[i].end());
int s = a[i];
go(x, u[i])c.pb(mp(node{s + x.fi, s}, x.se)), s += x.fi;
}
}
sort(c.begin(), c.end());
k = min(k, si(c));
rep(i, 0, k - 1)ed.pb(c[i].se);
sort(ed.begin(), ed.end(), [](int x,int y){return o[x] < o[y];});
printf("%lld\n", k);
go(x, ed)printf("%lld ", x);
return 0;
}