首页 > 其他分享 >题解 P5326【[ZJOI2019] 开关】/ SS241023B【判断题】

题解 P5326【[ZJOI2019] 开关】/ SS241023B【判断题】

时间:2024-10-23 21:42:19浏览次数:1  
标签:P5326 int 题解 sum mathscr ZJOI2019 modint hat rhs

已经沦落为可以随便搬进模拟赛的模板题了。。。

题目描述

当前有 \(n\) 道判断题,初始全选的错。初始给出每道题的正确答案,设 \(0\) 表示错,\(1\) 表示对。每道题有一个参数 \(p_i\),每轮会以 \(\frac {p_i} {\sum_{j = 1} ^ {n} p_j}\) 的概率选择第 \(i\) 道题并修改(flip)这道题的答案。问期望经过多少轮以后,\(n\) 道题与正确答案完全一致,答案对 \(998244353\) 取模。记 \(a_i\) 表示第 \(i\) 道题的正确答案。

保证对于所有数据满足:\(1 \leq n \leq 100\),\(\sum p_i \leq 5 \times 10 ^ 5\),\(p_i \geq 1\)。

概率生成函数基础理论

使用 PGF(概率生成函数)刻画原问题。PGF 是一个无穷项的 OGF(注意是 OGF),其 \([z^i]\) 项系数表示 \(i\) 步达成某种目标的概率,表示为 \(F(z)=\sum_{i\geq 0}f_ix^i\)。若已知达成某种目标(事件)的 PGF 为 \(F(z)=\sum_{i\geq 0}f_ix^i\),则达成这种目标的期望步数为 \(\sum_{i\geq 0}f_i\cdot i=F'(1)\)。

  • 从初态到终态的 PGF 为 \(F(z)\);
  • 从终态到终态的 PGF 为 \(G(z)\);
  • 从初态首次到终态的 PGF 为 \(H(z)\)(此前 \(F, G\) 均没有强调首次,表示可以经过很多次)。

则可以得知:\(H(z)\cdot G(z)=F(z)\) 即 \(H(z)=\dfrac{F(z)}{G(z)}\)。我们要求的从初态首次到达终态的期望步数即为

\[H'(1)=\left.\left(\frac{F(z)}{G(z)}\right)'\right|_{z=1}=\left.\frac{F'(z)G(z)-F(z)G'(z)}{G^2(z)}\right|_{z=1}=\frac{F'(1)G(1)-F(1)G'(1)}{G^2(1)} \]

以上都是理想情况,现实中 \(F(z), G(z)\) 不一定那么好算,甚至 \(H(1)\) 的计算也可能需要动手脚,但是分式求导几乎总会出现。很多情况下(几乎任何情况下都)会出现 \(F(1), G(1)\) 不收敛的情况,但因为答案是收敛的,此时一般可以修改 \(F(z), G(z)\),使它们额外乘上 \((1-z)\) 的因式,以去除某些值为 \(0\) 的分母。

拉普拉斯算子

有时需要将 EGF 转换为 OGF。记 \(\hat F(z)\) 为 \(F(z)\) 的 EGF 形式(即 \(F(z)=\sum_{i\geq 0}f_ix^i, \hat F(z)=\sum_{i\geq 0}f_ix^i/i!\))。前人从不知道哪个地方偷了个称作“拉普拉斯算子”(Laplace Operator)的符号 \(\mathscr L\)(\mathscr L)拿来用,并赋予其新定义 \(\mathscr L\hat F(z)=F(z)\),实现 EGF 向 OGF 的转化。可以感受到

  1. \(\mathscr L\) 是线性算子,\(\mathscr L(a\hat F(z)+b\hat G(z))=a\mathscr L\hat F(z)+b\mathscr L\hat G(z)\)。
  2. \(\mathscr Lae^{bz}=\dfrac{a}{1-bz}\),因为它们都指名同一个序列 \(\{a, ab, ab^2, ab^3, ab^4,\cdots\}\),前者 \(ae^{bz}\) 是 EGF 形式,后者 \(\dfrac{a}{1-bz}\) 是 OGF 形式。

solution

沿用上文记号,但是 OGF 不好刻画某个步数下的操作序列,EGF 才好刻画(我们需要 EGF 作为二项卷积的载体)。

限制即为第 \(i\) 道题目要被修改的次数 \(\equiv a_i\pmod 2\)。记 \(s=\sum_{i=1}^np_i\)。不演了,直接写 \(\hat F(z), \hat G(z)\),希望读者自己说明其意义:

\[\hat F(z)=\prod_{i=1}^n\frac{e^{p_i/s}+(-1)^{a_i}e^{-p_i/s}}{2} \]

\[\hat G(z)=\prod_{i=1}^n\frac{e^{p_i/s}+e^{-p_i/s}}{2} \]

由于 \(O(ns)\) 可接受,我们可以暴力计算并展开 \(\hat F(z), \hat G(z)\) 写为

\[\hat F(z)=2^{-n}\sum_{w=-s}^sa_we^{w/s} \]

\[\hat G(z)=2^{-n}\sum_{w=-s}^sb_we^{w/s} \]

的形式,这样的好处是我们可以将这个 EGF 转为 OGF。

\[\mathscr L\hat F(z)=F(z)=2^{-n}\sum_{w=-s}^{s}\frac{a_w}{1-wz/s} \]

\[\mathscr L\hat G(z)=G(z)=2^{-n}\sum_{w=-s}^{s}\frac{b_w}{1-wz/s} \]

这样以后我们就可以计算 \(H'(1)\) 了,需要计算 \(F(1), G(1), F'(1), G'(1)\)。但正如你所见当 \(z=1,w=s\) 时 \(\frac{a_w}{1-wz/s}\) 的分母为 \(0\),十分滑稽。这里的修正方法是使 \(F(z), G(z)\) 都乘 \((1-z)\) 使刚才说的那一项变为 \(a_s\) 这个常数,那么就可以计算了。这里需要对 \(F(z), G(z)\) 求导,其实也是一样用求导除法公式,参见上文 \(H'(z)\) 部分,这里需要你用草稿纸算好式子再写到程序里。

复杂度 \(O(ns)\)。提示:还有很多 PGF 题在 Atcoder 等待挑战(

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
template <unsigned umod>
struct modint {// {{{
  static constexpr int mod = umod;
  unsigned v;
  modint() = default;
  template <class T, enable_if_t<is_integral<T>::value, int> = 0>
    modint(const T& _v): v((unsigned)(_v % mod + (_v < 0 ? mod : 0))) {}
  modint& operator+=(const modint& rhs) { v += rhs.v; if (v >= umod) v -= umod; return *this; }
  modint& operator-=(const modint& rhs) { v -= rhs.v; if (v >= umod) v += umod; return *this; }
  modint& operator*=(const modint& rhs) { v = (unsigned)(1ull * v * rhs.v % umod); return *this; }
  modint& operator/=(modint rhs) {
    assert(rhs.v); 
    static constexpr int lim = 1 << 20;
    while (rhs.v >= lim) *this *= mod - mod / rhs.v, rhs.v = umod % rhs.v;
    static vector<modint> inv{0, 1};
    while (rhs.v >= inv.size()) {
      auto m = inv.size();
      inv.resize(m << 1);
      for (auto i = m; i < m << 1; i++) inv[i] = (mod - mod / i) * inv[mod % i];
    }
    return *this *= inv[rhs.v];
  }
  friend modint operator+(modint lhs, const modint& rhs) { return lhs += rhs; }
  friend modint operator-(modint lhs, const modint& rhs) { return lhs -= rhs; }
  friend modint operator*(modint lhs, const modint& rhs) { return lhs *= rhs; }
  friend modint operator/(modint lhs, const modint& rhs) { return lhs /= rhs; }
  explicit operator bool() const { return v != 0; }
  friend int raw(const modint& self) { return self.v; }
  friend ostream& operator<<(ostream& os, const modint& self) { return os << raw(self); }
  friend modint qpow(modint a, LL b) {
    modint r = 1;
    for (; b; b >>= 1, a *= a) if (b & 1) r *= a;
    return r;
  }
};// }}}
using mint = modint<998244353>;
int n, s[110], p[110], ph;
mint* alloc(int siz) {
  static mint _[10000010], *_ptr = _;
  return (_ptr += siz << 1 | 1) - siz;
}
mint *a = alloc(5e4), *b = alloc(5e4), *tmp = alloc(5e4);
int main() {
#ifndef LOCAL
  cin.tie(nullptr)->sync_with_stdio(false);
#endif
  cin >> n;
  for (int i = 1; i <= n; i++) cin >> s[i];
  for (int i = 1; i <= n; i++) cin >> p[i], ph += p[i];
  a[0] = 1, b[0] = 1;
  for (int i = 1; i <= n; i++) {
    for (int j = -ph; j <= ph; j++) tmp[j] = exchange(a[j], 0);
    // a: exp(p[i] / ph)
    for (int j = ph; j - p[i] >= -ph; j--) a[j] += tmp[j - p[i]];
    // a: (-1)^s[i] * exp(-p[i] / ph)
    for (int j = -ph; j + p[i] <= ph; j++) a[j] += tmp[j + p[i]] * (s[i] ? -1 : 1);
    for (int j = -ph; j <= ph; j++) tmp[j] = exchange(b[j], 0);
    // b: exp(p[i] / ph)
    for (int j = ph; j - p[i] >= -ph; j--) b[j] += tmp[j - p[i]];
    // b: exp(-p[i] / ph)
    for (int j = -ph; j + p[i] <= ph; j++) b[j] += tmp[j + p[i]];
  }
  mint f1 = a[ph], g1 = b[ph], fd1 = 0, gd1 = 0;
  for (int j = -ph; j <= ph - 1; j++) fd1 += a[j] / (mint{j} / ph - 1);
  for (int j = -ph; j <= ph - 1; j++) gd1 += b[j] / (mint{j} / ph - 1);
  cout << (fd1 * g1 - gd1 * f1) / (g1 * g1) << endl;
  return 0;
}

标签:P5326,int,题解,sum,mathscr,ZJOI2019,modint,hat,rhs
From: https://www.cnblogs.com/caijianhong/p/18498434/solution-P5326

相关文章

  • [COCI2009-2010#4] PALACINKE 题解
    前言题目链接:洛谷。题意简述\(n\)个点,\(m\)条边。每条边上有商店,经过一条边花费\(1\)单位时间,如果在边上的商店购物,额外花费\(1\)单位时间。需要购买\(4\)种物品,每个商店售出\(1\sim4\)种物品不等。请问,在\(T\)个单位时间内,从\(1\)出发购物,得到这\(4\)种物品......
  • [算法题解] Codeforces round 979 --D. QED's Favorite Permutation
    题目链接:https://codeforces.com/contest/2030/problem/D题目:题解:FavotitePermutation即每个数满足i=p[i];此题中,p数组每次询问后保持不变,s根据询问而改变。因此每次需要进行交换的数的位置是相同的,只需要检验s变更后,操作能否满足交换需求与否。故创建需数组need,预处理nee......
  • 题解:CF1225E Rock Is Push
    很玄妙的一道dp题。HintAnalysis首先你要确保你会做当石头没有/固定的情况,这道题其实也是dp。考虑石头带来的影响,唯一的作用就是限制你的移动,比方说你下面有\(3\)块石头,由于只能向右或向下移动,你实际上往下只能走到当前列第\(n-3\)行。于是对于石头的处理,设\(rs[i][j......
  • 题解:P11215 【MX-J8-T3】水星湖
    依旧是模拟赛赛题。HintAnalysis首先你注意到两棵相邻的树是一定不会死的,所以可能会死的只有自己种下去的树,队列维护。接着考虑对于每个位置,\(\text{bfs}\)维护一个最小的长出树的时间\(vis[i][j]\),最后暴力统计答案即可。具体细节看注释。Code#include<bits/stdc++.h>......
  • CRC32爆破脚本 + [MoeCTF 2022]cccrrc 题解
    CRC32爆破原理介绍:CRC(循环冗余校验)是一种用于检测数据传输错误的技术。CRC算法生成一个校验值(校验和),这个值可以附加到数据后面,在数据接收方重新计算校验值并与附加的校验值进行比较,以此来确定数据是否在传输过程中发生了错误CRC32是一种常用的CRC算法,它的校验值长度固定为3......
  • P7910 [CSP-J 2021] 插入排序 题解
    正解首先要注意$2$点:修改数组元素的值会影响接下来的操作.对数组进行排序不会影响接下来的操作.思路直接扫一遍数组.假设排序后$a_x$会在第$p$位上.将$p$初始化为$n$.然后就开始找$x$前后有多少个小于$a_x$的值就行了.时间复杂度:$\Theta(nq)$.注意......
  • P7911 [CSP-J 2021] 网络连接 题解
    模拟代码#include<iostream>#include<cstdio>#include<cstring>usingnamespacestd;intn,p=1,ans[1003];//没事干的ans数组structnode{ stringop,ad;}a[1003];intws(intn){//位数 intsum=0; if(n==0)return1; while(n){ n......
  • P8816 [CSP-J 2022] 上升点列 题解
    最长上升子序列根据题目中,每个坐标的横纵坐标均单调递增,所以明显可以使用最长上升子序列.定义状态$f_{i,p}$,表示正在节点$i$时,还剩下$p$次插入机会,所能达到的最大长度.定义变量$dis=|x_i-x_j|+|y_i-y_j|-1.$,表示$i$到$j$节点至少要插$dis$个节点.为什么要$-1$......
  • ARC165F题解
    前言\(2024.10.19\)日校测\(T4\),思维太庙,被薄纱了,遂哭弱,写题解以记之。简要题意给你一个长度为\(2n\)的序列\(A,\foralla_i\in[1,n]\),其中\(1\)到\(n\)每个数都出现了两次,现在需要把相同的两个数排到一起,每次操作只能交换相邻两个数,在保证操作次数最小的情况下求出现......
  • P8814 [CSP-J 2022] 解密 题解
    解方程$题目中说,n=pq,ed=(p-1)(q-1)+1,m=n-ed+2.$$把ed的式子展开,得到:$$ed=p(q-1)-(q-1)+1$$ed=pq-p-q+2$$再把展开后的式子带入m中,得:$$m=n-(pq-p-q+2)+2.$$m=n-pq+p+q-2+2$$\becausen=pq$$\thereforem=pq-pq+p+q-2+2$$m=p+q.$$如果想要求出p和q的值,那么可以再......