[??记录] CF626G Raffles
不考虑修改,首先有一个贪心做法:
每次找到一个还能押彩票且收益最大的奖池,押一票,用堆维护可做到 \(O(n \log n)\)。
对于奖池 \(i\) ,若目前已押 \(x\) 张票,则再押一张的收益期望是:
\[\Delta E(x+1) = p_i(\dfrac {x + 1}{x + l_i + 1} - \dfrac {x} {x + l_i}) = \dfrac{p_i l_i}{(x+l_i+1)(x+l_i)}
\]
若 \(l_i + 1\) ,则收益期望变为:
\[\Delta E'(x+1) = p_i(\dfrac {x+1}{x+l_i + 2} - \dfrac {x}{x+l_i+1} = \dfrac{p_i(l_i+1)}{(x+l_i+2)(x+l_i+1)}
\]
押第 \(x\) 张票的收益期望变为:
\[\Delta E'(x) = p_i(\dfrac {x}{x+l_i+1} - \dfrac {x-1}{x+l_i}) = \dfrac {p_i(l_i+1)}{(x+l_i+1)(x+l_i)}
\]
对比 \(\Delta E'(x)\) 和 \(\Delta E(x+1)\) 可以发现 \(\Delta E(x+1) < \Delta E'(x)\),也就是说:
如果 \(l_i + 1\) 之前奖池 \(i\) 押了 \(x+1\) 张票,\(l_i + 1\) 后奖池 \(i\) 至少也会押 \(x\) 张票。
因此每次修改 \(l_i\) 时,只需要看将收益期望最低的换成其他票能不能更优即可。
时间复杂度 \(O(q \log n)\) 。
\(\texttt{Code:}\)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
struct st {
double d; int id;
bool operator < (const st &a) const {
return d != a.d ? d > a.d : id < a.id;
}
};
struct st2 {
double d; int id;
bool operator < (const st2 &a) const {
return d != a.d ? d < a.d : id < a.id;
}
};
multiset<st> q;
multiset<st2> q2;
int n, t, m, p[N], l[N], cnt[N], rst;
double ans;
double calc(int i) {
// assert(cnt[i] + l[i] > 0);
return 1.0 * p[i] / (cnt[i] + l[i]) * l[i] / (cnt[i] + l[i] + 1);
}
double calc2(int i) {
// assert(cnt[i] + l[i] > 1);
return 1.0 * p[i] / (cnt[i] + l[i] - 1) * l[i] / (cnt[i] + l[i]);
}
void add() {
while (rst) {
if (q.empty()) break;
rst--;
st cur = *q.begin(); q.erase(cur);
if (cnt[cur.id]) q2.erase((st2){calc2(cur.id), cur.id});
cnt[cur.id]++; ans += cur.d;
// printf("%d %d %d %.10lf\n", rst, cur.id, cnt[cur.id], ans);
q2.insert((st2){calc2(cur.id), cur.id});
if (cnt[cur.id] < l[cur.id]) q.insert((st){calc(cur.id), cur.id});
}
}
int main() {
scanf("%d%d%d", &n, &rst, &m);
for (int i = 1; i <= n; i++) scanf("%d", &p[i]);
for (int i = 1; i <= n; i++) scanf("%d", &l[i]);
for (int i = 1; i <= n; i++) q.insert((st){calc(i), i});
add();
// printf("%d %.10lf\n", rst, ans);
while (m--) {
int opt, x; scanf("%d%d", &opt, &x);
if (opt == 1) {
if (rst) {
q2.erase((st2){calc2(x), x});
cnt[x]++; l[x]++; rst--;
q2.insert((st2){calc2(x), x});
} else {
if (cnt[x] < l[x]) q.erase((st){calc(x), x});
if (cnt[x]) q2.erase((st2){calc2(x), x});
// assert(cnt[x] + l[x] > 0);
ans -= 1.0 * p[x] * cnt[x] / (cnt[x] + l[x]);
l[x]++;
ans += 1.0 * p[x] * cnt[x] / (cnt[x] + l[x]);
if (cnt[x]) q2.insert((st2){calc2(x), x});
q.insert((st){calc(x), x});
st2 cur = *q2.begin(); q2.erase(cur);
if (cnt[cur.id] < l[cur.id]) q.erase((st){calc(cur.id), cur.id});
ans -= cur.d; cnt[cur.id]--;
if (cnt[cur.id]) q2.insert((st2){calc2(cur.id), cur.id});
q.insert((st){calc(cur.id), cur.id});
rst++;
// printf("???%d %.10lf %d\n", cur.id, ans, rst);
add();
}
} else {
if (q.empty()) {
q2.erase((st2){calc2(x), x});
rst++; l[x]--; cnt[x]--;
q2.insert((st2){calc2(x), x});
} else {
if (cnt[x] < l[x]) q.erase((st){calc(x), x});
if (cnt[x]) q2.erase((st2){calc2(x), x});
// assert(cnt[x] + l[x] > 0);
ans -= 1.0 * p[x] * cnt[x] / (cnt[x] + l[x]);
l[x]--;
// assert(cnt[x] + l[x] > 0);
if (cnt[x] > l[x]) cnt[x]--, rst++;
ans += 1.0 * p[x] * cnt[x] / (cnt[x] + l[x]);
if (cnt[x]) q2.insert((st2){calc2(x), x});
if (cnt[x] < l[x]) q.insert((st){calc(x), x});
st2 cur = *q2.begin(); q2.erase(cur);
if (cnt[cur.id] < l[cur.id]) q.erase((st){calc(cur.id), cur.id});
ans -= cur.d; cnt[cur.id]--;
if (cnt[cur.id]) q2.insert((st2){calc2(cur.id), cur.id});
q.insert((st){calc(cur.id), cur.id});
rst++;
// printf("???%d %.10lf %d\n", cur.id, ans, rst);
add();
}
}
printf("%.8lf\n", ans);
}
return 0;
}

浙公网安备 33010602011771号