首页 > 其他分享 >CF626G Raffles 题解

CF626G Raffles 题解

时间:2024-07-28 17:42:00浏览次数:7  
标签:emplace getdet Raffles int 题解 st erase f64 CF626G

Description

  • 有 \(n\) 个奖池,第 \(i\) 个奖池的奖金是 \(p_i\),已经有 \(l_i\) 张彩票押在上面。
  • 现在你有 \(t\) 张彩票,你需要将你的彩票分配到这些奖池中,并且保证你在每个奖池中押的彩票数不能超过该奖池原有的彩票数
  • 若你在第 \(i\) 个奖池中押了 \(t_i\) 张彩票,则你中奖的概率为 \(\frac{t_i}{t_i + l_i}\),若你中奖,你可以获得这个奖池的全部奖金 \(p_i\)。
  • 一共有 \(q\) 次事件,每次事件会使某个 \(l_i\) 加 \(1\) 或减 \(1\)。
  • 你需要在每个事件后求出在最佳方案下你获得的奖金总数的最大期望值。
  • \(n,t,q \le 2 \times 10^5\),\(p_i,l_i \le 10^3\),答案精度误差 \(\le 10^{-6}\)。

Solution

首先单次 \(O(t\log n)\) 是很容易的,就先假设所有 \(t_i=0\),每次取出让 \(t_i\to t_i+1\) 的增量的最大值,由于对于每个 \(i\),\(t_i\) 越大增量越小,所以每次贪心取最大值是对的。

考虑怎么支持修改。

不妨设修改了 \(x\),让 \(l_x\leftarrow l_x+1\)。

有一个结论是每次选出当前已经选择的 \(\Delta\) 里的最小值和没选的 \(\Delta\) 的最大值,如果这个最小值小于最大值就贪心地替换,替换的次数不超过 \(1\) 次。

证明就考虑设 \(\Delta E(k)=\dfrac{p_xl_x}{(l_x+k)(l_x+k+1)},\Delta E'(k)=\dfrac{p_x(l_x+1)}{(l_x+k+1)(l_x+k+2)}\)。

注意到 \(\Delta E'(k)=\dfrac{p_x(l_x+1)}{(l_x+k+1)(l_x+k+2)}>\Delta E(k+1)=\dfrac{p_xl_x}{(l_x+k+1)(l_x+k+2)}\),所以 \(x\) 只有可能是选了的最小的增量被替换,所以最多只有一次。

对于 \(l_x\leftarrow l_x-1\) 的情况同理可证结论仍成立。

时间复杂度:\(O((n+q+t)\log n)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

using f64 = long double;

const int kMaxN = 2e5 + 5;

int n, m, q;
f64 ans;
int l[kMaxN], p[kMaxN], t[kMaxN];
std::multiset<std::pair<f64, int>> st[2];

f64 getdet(int x, int v) {
  return (f64)p[x] * l[x] / (l[x] + t[x]) / (l[x] + t[x] + v);
}

void add(int x) {
  --m;
  st[1].erase({getdet(x, 1), x});
  if (t[x]) st[0].erase({getdet(x, -1), x});
  ans += getdet(x, 1), ++t[x];
  st[0].emplace(getdet(x, -1), x);
  if (t[x] < l[x]) st[1].emplace(getdet(x, 1), x);
}

void del(int x) {
  ++m;
  if (t[x] < l[x]) st[1].erase({getdet(x, 1), x});
  st[0].erase({getdet(x, -1), x});
  ans -= getdet(x, -1), --t[x];
  if (t[x]) st[0].emplace(getdet(x, -1), x);
  st[1].emplace(getdet(x, 1), x);
}

void dickdreamer() {
  std::cin >> n >> m >> q;
  for (int i = 1; i <= n; ++i) std::cin >> p[i];
  for (int i = 1; i <= n; ++i) std::cin >> l[i];
  for (int i = 1; i <= n; ++i) st[1].emplace(getdet(i, 1), i);
  for (; m && !st[1].empty();) add(st[1].rbegin()->second);
  for (int i = 1; i <= q; ++i) {
    int op, x;
    std::cin >> op >> x;
    if (op == 1) {
      if (t[x]) st[0].erase({getdet(x, -1), x});
      if (t[x] < l[x]) st[1].erase({getdet(x, 1), x});
      ans -= (f64)p[x] * t[x] / (t[x] + l[x]);
      ++l[x];
    } else {
      if (t[x] == l[x]) del(x);
      if (t[x]) st[0].erase({getdet(x, -1), x});
      if (t[x] < l[x]) st[1].erase({getdet(x, 1), x});
      ans -= (f64)p[x] * t[x] / (t[x] + l[x]);
      --l[x];
    }
    ans += (f64)p[x] * t[x] / (t[x] + l[x]);
    if (t[x]) st[0].emplace(getdet(x, -1), x);
    if (t[x] < l[x]) st[1].emplace(getdet(x, 1), x);
    for (; m && !st[1].empty();) add(st[1].rbegin()->second);
    if (!st[0].empty() && !st[1].empty()) {
      auto [det1, j1] = *st[0].begin();
      auto [det2, j2] = *st[1].rbegin();
      if (det1 < det2) del(j1), add(j2);
    }
    std::cout << std::fixed << std::setprecision(10) << ans << '\n';
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:emplace,getdet,Raffles,int,题解,st,erase,f64,CF626G
From: https://www.cnblogs.com/Scarab/p/18328571

相关文章

  • AtCoder Beginner Contest 363 题解 A-D(待补充)
    A-PilingUp1.1思路其实就是向上取百位的整,需要增加多少,123则为200-123=177;1.2代码voidsolve(){intn;cin>>n;intt=n/100;cout<<(t+1)*100-n;}B-JapaneseCursedDoll 2.1思路就是判断最少需要多少天,会有大于等于P个人......
  • Codeforces Round 962 (Div. 3) 题解 A-F
    A.LegsProblem-A-Codeforces1.1翻译农夫约翰的农场又迎来了美好的一天。农夫约翰来到农场后,数了数n条腿。众所周知,农场里只住着鸡和牛,一只鸡有2条腿,而一头牛有4条腿。假设约翰农场主数清了所有动物的腿,那么他的农场里最少有多少动物?1.2思路求最少有几只动物......
  • 题解 - 矩阵
    题目描述小明和小花知道学信息学竞赛的学生特别擅长做一些和矩阵相关的问题。例如,同学经常做的一个题目,给你一个N×M的矩阵,矩阵里面每个格子上都有一个数,要从左上角(1,1),走到右下角(N,M),每一步只能往下或者往右走,让你求经过的路径上数的总和最小。小明和小花发现这个......
  • luogu P1896 [SCOI2005] 互不侵犯 题解
    luoguP1896[SCOI2005]互不侵犯题解题目传送门思路状态压缩dp。状态压缩dp对于每一行,用一个\(n\)位二进制数表示每行的状态,则对于上下两行之间,设上行的数字为\(a\),下行的数字为\(b\),状态不合法有三种情况:\(a\operatorname{and}b\neq0\),即存在上行与下行同......
  • 洛谷P2440 题解
    P2440题解题目传送门提取关键词,题目需要的是数量大于$k$的最大$l$,考虑二分答案。可以二分枚举$l$的值,check函数中检验切割出该长度小段木头的个数,与$k$进行比较。小数据模拟例如,我们有五根木棍且$k=4$,分别为374106第一次循环$l=0,r=9,mid=4$。check函数中得到......
  • luogu P1896 [SCOI2005] 互不侵犯 题解
    luoguP1896[SCOI2005]互不侵犯题解题目传送门思路状态压缩dp。状态压缩dp对于每一行,用一个\(n\)位二进制数表示每行的状态,则对于上下两行之间,设上行的数字为\(a\),下行的数字为\(b\),状态不合法有三种情况:\(a\operatorname{and}b\neq0\),即存在上行与下行同......
  • Codeforces Round 962 (Div. 3) A - D详细题解(思路加代码Python,C++(垃圾灰名小白想
             吐槽一下,这次比赛不知道怎么的,可能是div3参加的人比较多吗,代码题解上去后全是inqueue,比赛的过程中我还看了提交的,80多页几千个提交全是inqueue,我的代码等了**半个多小时才运行,然后发现timelimit真的有点搞心态,思路在下一题我还要反过来去优化上一题,不过......
  • 逆序对的数量 - 题解
    逆序对的数量时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++64MB,其他语言128MB描述给定一个长度为\(n\)的整数数列,请你计算数列中的逆序对的数量。逆序对的定义如下:对于数列的第\(i\)个和第\(j\)个元素,如果满足\(i<j\)且\(a[i]>a[j]\),则其为一个逆序对;否则......
  • ABC364题解(D-G)
    D先对\(a\)从小到大排序。将题目转化成找到最小的\(d\),使得恰好有\(k\)个\(a_i\in[b-d,b+d]\)。对于每个询问\(b,k\),考虑二分答案。设待检查的答案为\(d\),二分找到最小的\(p1\)使得\(a_{p1}\geqb-d\)和最小的\(p2\)使得\(a_{p2}>b+d\),包含的数的个数即为\(......
  • 题解:P10481 Sudoku
    Sudoku来自蓝书思路优化搜索顺序,位运算常数优化。优化搜索顺序数独问题的搜索很简单,“搜索状态”就是每个位置上填了什么数。在每个状态下,选择一个未填位置,检查那些填入仍能满足数独条件(合法)的数字。构成的新的局面即为该状态的“分支”。足够暴力的写法为依次对每个位置进......