[??记录] CF626G Raffles

\(\texttt{link}\)

不考虑修改,首先有一个贪心做法:

每次找到一个还能押彩票且收益最大的奖池,押一票,用堆维护可做到 \(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;
}
posted @ 2021-10-28 09:15  klii  阅读(48)  评论(0)    收藏  举报