题目:[[2024.10.16 题目]] ## 最终测试 $n$ 名选手正在参加比赛,第 $i$ 名选手作答了两道题,分值为 $a_{i,1}$ 与 $a_{i,2}$,每道题有 $50\%$ 的正确概率,求每个选手最终排名的期望,误差不超过 $10^{-6}$。选手的排名定义为比他分数高的人数 $+1$ 的值。 --- 本题 $1\le n\le 1\times 5$,很显然出题人想要我们使用 $\mathcal O(n)$ 或 $\mathcal O(n\log_2 n)$ 解法。每个人有两道题,每道题有两种可能的状态,因此不难想到将一个人拆成四份(即四种不同的总分),这个人的期望就是每一份的期望的平均值。 接下来考虑怎么求每一份的期望。既然别人也可以有四份,那么我们就把别人的四份总分 $x$ 对应在数轴上的点计数器 $+1$,然后大于每一份的值的点的数量就是排名的总和,除以 $4$ 得到每一份的期望。然后再除以 $4$ 即可得到每个人的期望。由于第一名排名为 $1$,因此答案需要额外 $+1$。 该算法的时间复杂度为 $\mathcal O(n^2)$。不难想到使用树状数组来维护数轴上的点的数量(连离散化都不需要),先把每个人的每一份都加进去,然后对于每一个人 $i$ 将 $i$ 的 $4$ 份从树状数组中删除,再根据上面的过程模拟即可。时间复杂度 $\mathcal O(n\log_2 n)$。 ```cpp #include <fstream> #include <iomanip> using namespace std; using LL = long long; const LL kMaxN = 1e5 + 5; ifstream cin("test.in"); ofstream cout("test.out"); LL a[kMaxN][3], t[kMaxN], n; void modify(LL x, LL y) { for (x++; x <= 1e5; x += x & -x) { t[x] += y; } } LL query(LL x) { LL res = 0; for (x++; x; x -= x & -x) { res += t[x]; } return res; } LL get(LL x) { return query(1e5) - query(x); } void fuck(LL a, LL b, LL v) { modify(0, v); modify(a, v); modify(b, v); modify(a + b, v); } int main() { cin.tie(0)->sync_with_stdio(0); cin >> n; for (LL i = 1; i <= n; i++) { cin >> a[i][1] >> a[i][2]; fuck(a[i][1], a[i][2], 1); } for (LL i = 1; i <= n; i++) { fuck(a[i][1], a[i][2], -1); cout << fixed << setprecision(6) << (get(0) + get(a[i][1]) + get(a[i][2]) + get(a[i][1] + a[i][2])) / 16.0 << '\n'; fuck(a[i][1], a[i][2], 1); } return 0; } ``` ## 空间跳跃 给定正整数 $d,l$,初始有整数 $n=1$,可以做以下三种操作: - 将 $n$ 改变为 $2\times n$; - 若 $\min(|n|,|n-d|)\le l$,可以将 $n$ 改为 $n-d$; - 若 $n\equiv 1\pmod 3$,则可以将 $n$ 改为 $\cfrac{n-1}{3}$。 $q$ 次询问,每次询问给出目的地 $x$,求变换方案使得 $n=1$ 最终能够变换为 $x$。输出方案,变换次数不能超过 $1500$ 次。 --- 仔细观察操作 $1$ 和操作 $3$,不难发现它正好为冰雹猜想的翻转版本。(冰雹猜想:对于任意正整数 $n$,若 $n$ 为偶数则 $n\gets \cfrac{n}{2}$,否则 $n\gets 3\times n+1$。在有限次操作内,$n$ 最终变为 $1$)那么,对于任意 $x\ge 1$,我们直接使用冰雹猜想即可;对于 $x=0$,我们让 $x\gets 3\times x+1$ 也可以。 现在最大的问题就是如何使得一个负数 $x<0$ 快速变为非负整数,因为冰雹猜想并不能在负数下成立,会在某几个数字间不断循环。观察第二种操作,在看看数据范围,发现 $1\le l\le 10^3$,这就代表着我们只需要变换到 $x\ge -10^3$ 即可直接使用第二种操作将其变为非负整数。根据验证,任意 $0\le x\le 10^6$ 都可以在不超过五百次操作 $1$ 和操作 $3$ 下变为 $1$。此时,题目已经解出来了,但是我们来考虑下负数情况下冰雹猜想的性质。 根据模拟,可以发现 $x<0$ 时 $x$ 会在 $0,-1,-5,-17$ 中循环,这也是为什么大多数题解都对这几个数进行了特判,如果满足则循环使用第二种性质,但是似乎没有题解指出这么做的意义何在。 ```cpp #include <fstream> #include <algorithm> #include <vector> using namespace std; using LL = long long; ifstream cin("jump.in"); ofstream cout("jump.out"); LL q, d, l, x; vector<LL> ans; int main() { cin.tie(0)->sync_with_stdio(0); for (cin >> q >> d >> l; q; q--) { cin >> x; ans.clear(); if (x < 1) { // Shabi Bingbao Caixiang while (x < -1e3) { // 亦可设置成其它较大负数,如 -114 ans.push_back(x); x = x % 2 ? x * 3 + 1 : x / 2; } // Zhijie Jia while (x < 1) { ans.push_back(x); x += d; } } // Shabi Bingbao Caixiang while (x != 1) { ans.push_back(x); x = x % 2 ? x * 3 + 1 : x / 2; } cout << ans.size() << ' '; ans.push_back(x); reverse(ans.begin(), ans.end()); for (LL i : ans) { cout << i << ' '; } cout << '\n'; } return 0; } ``` ## 快速访问 有 $n+1$ 个结点的树,结点编号为 $0,1,\cdots,n$,第 $i$ 条边连接 $u_i$ 与 $v_i$。求对于 $i$ ${} (1\le i\le n) {}$,$A_i=\sum\limits_{j=S_i}\operatorname{dis}(i,j)^2$ 的值,其中 ${} S_i=\{j\in\mathbb{Z}|\max(1,i-1)\le j<i\}\cup\{0\} {}$,$\operatorname{dis}(i,j)$ 为 $i$ 到 $j$ 的简单路径经过的边的数量。 --- 采用暴力。通过 dfs 预处理+倍增方法快速求出任意两点的最近公共祖先,在配合题意模拟并计算满足条件的两点长度和。时间复杂度 $\mathcal O(n^2\log_2 n)$,可以通过 Tarjan 的离线算法达到 $\mathcal O(n^2)$。得到 $40$ 分。 ## 门童 牛牛去当志愿者。大厅沙发和大门距离为 $L$ 个单位,牛牛在每秒可以做以下事: - 站在门口不动,开心度每秒减少 $x_1$; - 从门口走向沙发一个单位,开心度每秒减少 $x_2$; - 从沙发走向门口一个单位,开心度每秒减少 $x_3$; - 在沙发上摸鱼,开心度每秒增加 $x_4$。 有 $n$ 名选手需要牛牛接待,第 $i$ 名选手会在第 $t_i$ 秒到达大门,它的耐心值为 $p_i$,友善值为 $f_i$,牛牛必须要在任意时刻 $T$ $(t_i\le T\le t_i+p_i)$ 接待,接待瞬间完成,开心值会在接待的瞬间上升 $f_i\times(t_i+p_i+T)$。 牛牛在第 $0$ 秒站在门口,开心度为 $0$。求牛牛在工作完成的时刻,开心度可以达到的最大值。 --- 没有完全看懂题目,代码都没有写,出题人我喜欢你。 **后记**:发现模拟牛牛一直在大门口可以得到 $75$ 分。天地人,你我他,数据人我\_\_\_你\_\_\_。 ## 总结 - **排名**:$2$; - **比赛分数**:$240/400$; - **情况**:相比 [[2024.10.14 模拟赛]],较好; - **问题**:容易红温。