Codeforces 521D Shop
https://codeforces.com/contest/521/problem/D
题意:
给 \(n\) 个数 \(a_i\),有 \(m\) 个操作,你需要最多使用其中 \(k\) 个,使得这 \(n\) 个数的积最大
三种操作分别为:
- assign,即把 \(a_i\) 变成 \(y\)
- add,即把 \(a_i\) 加上 \(y\)
- mulity,即把 \(a_i\) 乘上 \(y\)
题解:
从简单的操作开始考虑,对于3操作而言,无论前面如何操作,乘法总是直接对答案贡献 \(y\) 倍,此时加入加法,对答案的贡献是 \(\frac{a_i+y}{a_i}\) 倍,而且对于任何操作,先加后乘一定更优。而对于多个加法操作,取其中一些的话一定是先加大的更优。再考虑最复杂的赋值操作,赋值操作一定在加法之前会更优,然后他就相当于对原数做了一次差值的加法,而且多次赋值与一次赋值没有区别,所以一次赋值显然更优。所以我们按照乘法的贡献排序即可。
这里需要特别注意的一点是加法转乘法贡献虽然对于任意相同加法操作集合,他的顺序并不影响结果,但是我们仍然要按照从大到小的顺序计算贡献。这样才能保证大的贡献一定高于小的加法,排序之后才能保证取到更大的一些。
另外还可能存在的问题是如果把赋值操作也当作加法再按照贡献排序,赋值可能会出现在加法的后面,其实上面说过了对于任意相同加法操作集合,取值顺序并不影响结果,所以只要按权排序取到了赋值操作所在的加法,它放在第一个位置即可保证更优。
最后一个问题是它的计算最多会比较分子分母都为 \(1e11\) 级别的分数,目前cf可以使用 __int128
,或者 double 也能通过,然后如果把权值全体 -1,即把 \(\frac{x+f}{x}\) 变成 \(\frac{f}{x}\) 即可避免爆 int64
的问题。
代码:
/*================================================================
*
* 创 建 者: badcw
* 创建日期: 2020/6/22 7:14
*
================================================================*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n, ll mod = ::mod) {
ll res = 1;
while (n > 0) {
if (n & 1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
struct Frac {
__int128 a, b;
Frac(__int128 a_ = 1, __int128 b_ = 1) {
a = a_;
b = b_;
// getJian();
}
Frac operator + (const Frac& oth) {
return Frac(a * oth.b + oth.a * b, b * oth.b);
}
Frac operator * (const Frac& oth) {
a *= oth.a;
b *= oth.b;
// getJian();
return *this;
}
bool operator < (const Frac& oth) const {
return a * oth.b < b * oth.a;
}
bool operator == (const Frac& oth) const {
return a * oth.b == b * oth.a;
}
bool operator <= (const Frac& oth) const {
return a * oth.b <= b * oth.a;
}
bool operator > (const Frac& oth) const {
return a * oth.b > b * oth.a;
}
};
struct node {
int tt;
int x;
Frac pw;
bool operator < (const node& oth) const {
return pw > oth.pw;
}
};
struct op {
int tt;
int x;
int y;
bool operator < (const op& oth) const {
return y > oth.y;
}
};
int n, m, k;
int a[maxn];
int mx[maxn];
int mxpos[maxn];
vector<op> e[maxn];
template<class T, int g = 10>
void print(T x) {
vector<char> a(38);
int tot = 0;
while (x > 0) {
a[tot++] = x % g;
x /= 10;
}
for (int i = tot - 1; i >= 0; --i) putchar('0' + a[i]);
putchar('\n');
}
int main(int argc, char* argv[]) {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
static vector<node> deal;
for (int i = 1; i <= m; ++i) {
int op, x, y;
scanf("%d%d%d", &op, &x, &y);
if (op == 1) {
if (mx[x] < y) {
mx[x] = y;
mxpos[x] = i;
}
} else if (op == 2) {
e[x].push_back({1, i, y});
} else {
if (y == 1) continue;
deal.push_back({2, i, Frac(y, 1)});
}
}
for (int i = 1; i <= n; ++i) {
if (mx[i] > a[i]) e[i].push_back({0, mxpos[i], mx[i] - a[i]});
sort(e[i].begin(), e[i].end());
__int128 now = a[i];
for (auto j : e[i]) {
deal.push_back({j.tt, j.x, Frac(now + j.y, now)});
now += j.y;
}
}
k = min(k, (int)deal.size());
sort(deal.begin(), deal.end());
// sort(deal.begin(), deal.begin() + k, [](node a, node b) {
// return a.tt < b.tt;
// });
// printf("%d\n", k);
// for (int i = 0; i < k; ++i) printf("%d ", deal[i].x);
vector<int> res(k);
for (int i = 0; i < res.size(); ++i) res[i] = i;
sort(res.begin(), res.end(), [](int x, int y) {
return deal[x].tt < deal[y].tt;
});
printf("%d\n", (int)res.size());
for (auto i : res) printf("%d ", deal[i].x);
return 0;
}