2020百度之星程序设计大赛复赛
A. Battle for Wosneth (Hdu 6838)
题目大意
初始有无限血,有滴血。有命中,并使减少一滴血,自身回复一滴血。有概率命中,并使减少一滴血,但自身血不变。问当血量减少为时,的期望血量变化值是多少。结果对取模。
解题思路
当血量大于时,设命中一次,自身血量变化的期望值为,则(此处为小数)
解得
所以Bob从滴血扣到滴血时,Alice的血量变化期望值为
当剩下一滴血时,由于如果命中他,则Bob不会反击,这是与上方的区别所在,设命中Bob,自身血量变化值为,则
解得
所以最终答案
这可以理解为先假设轮,都会反击,造成变化期望值为,再减去最后一次实际反击的变化(扣血)期望。
即为
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const LL mo = 998244353; LL qpower(LL a, LL b) { LL qwq = 1; while (b) { if (b & 1) qwq = qwq * a % mo; b >>= 1; a = a * a % mo; } return qwq; } LL inv(LL x) { return qpower(x, mo - 2); } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int kase; cin >> kase; for (int ii = 1; ii <= kase; ii++) { LL m, q, p; cin >> m >> p >> q; p = p * inv(100) % mo; q = q * inv(100) % mo; LL ans = ((1 - q * inv(p) % mo + mo) % mo * m % mo + q) % mo; cout << ans << endl; } return 0; }
B. Binary Addition (Hdu 6839)
题目大意
给你一串无限长的串,其中第位及以后都是。现你有两种操作作用于串:
- 将某一位与异或
- 将其视为一个数,对它加一。其中最低位在最左边
求最小的操作次数,使得串变成串。
解题思路
这种看似麻烦的题要去想想特别之处。
可以证明猜测操作二要执行则仅可能执行一次。
操作二有什么用?
如果第一位是,操作二与操作一没区别。
如果第一位是,操作二就能够将前面一连串的变成,在这之后的变成
如果我们会执行两次操作二,由于执行了第一次操作二,前面的数变成了,我们要重新变成,才能再执行操作二。而这最终的结果也只是把某一位变成,而这一结局采用操作一可以一步到位。
所以我们就得到了个重要性质:操作二只能执行一次或者不执行
所以,我们就枚举操作二的执行效果,即枚举,把前个数变成,并把第个数变成,然后执行一次操作二,剩下的全部执行操作一即可。
设表示串的前个数中的个数,表示串的前个数中的个数,表示和串的中不同的数的个数。
则此时的次数就为
对所有以及取最小值即是答案。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 1e5 + 8; char s[N], t[N]; int n; int cnt[N]; int num0[N]; int num1[N]; int qwq(int pos) { return num0[pos] + (s[pos + 1] == 1) + 1 + num1[pos] + (t[pos + 1] == 0) + cnt[pos + 2]; } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { read(n); scanf("%s", s + 1); scanf("%s", t + 1); num0[0] = num1[0] = 0; for (int i = 1; i <= n; ++i) { s[i] -= '0'; t[i] -= '0'; num0[i] = num0[i - 1] + (s[i] == 0); num1[i] = num1[i - 1] + (t[i] == 1); } cnt[n + 1] = 0; cnt[n + 2] = 0; bool sign = false; int cur = n; for (int i = n; i >= 1; --i) { cnt[i] = cnt[i + 1] + (s[i] ^ t[i]); } int ans = cnt[1]; for (int i = 1; i <= n; ++i) { ans = min(ans, qwq(i)); } write(ans, '\n'); } return 0; }
C. Range k-th Maximum Query (Hdu 6840)
题目大意
给定一个个数的序列,以及正整数,要求对它重新排序,使得所有长度为的子区间的第大的数和和最大和最小。求最大值和最小值。
解题思路
我们将数列从大到小排列。前大的数不会对答案有贡献。
要让和最大,我们期望大的数对答案的贡献尽可能多。
于是我们可以构造这样的序列,它是由若干个长度为的区间构成。
每个这样的区间,前个位置标记为红,后个位置标记为蓝。
我们将这个排好序的序列,从左到右,按顺序填充蓝的位置,放完蓝的,然后再从右到左,按顺序填充红的位置。
最后一个长度不足的区间(如果有的话),前个位置标记为红,后个位置标记为蓝(如果有的话)。
这样就是最大值的构造。
最小值,将数列从小到大排列,前大的数也就是说前小的数,再按照上面构造就可以了。
既然构造出来了,答案自然也就能求出来了。
最大值的情况,设
排好序的数列里,的数都对答案有次的贡献,其中位置是的倍数的还有额外的次的贡献。
最后第位的贡献次数跟有关
如果,则第位到第的数对答案也有次的贡献。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 1e5 + 8; int n, k, l, d, r; LL a[N]; LL solve(LL a[], int k) { LL ans = 0; for (int i = k; i < d * k; ++i) { ans += a[i] * (1 + (l - k) * (i % k == 0)); } ans += a[d * k] * min(r + 1, l - k + 1); if (r > l - k) { for (int i = d * k + 1, yu = r - l + k; yu; ++i, --yu) { ans += a[i]; } } return ans; } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { read(n); read(l); read(k); for (int i = 1; i <= n; ++i) { read(a[i]); } sort(a + 1, a + 1 + n, greater<int>()); d = n / l; r = n % l; LL ans1 = solve(a, k); sort(a + 1, a + 1 + n); k = l - k + 1; LL ans2 = solve(a, k); printf("%lld %lld\n", ans1, ans2); } return 0; }
D. Link Cut Tree (Hdu 6841)
题目大意
qwq
解题思路
qwq
神奇的代码
qwq
E. Battle for Wosneth2 (Hdu 6842)
题目大意
初始有滴血,有滴血。有命中,并使减少一滴血,但自身血不变;有概率命中,并使减少一滴血,但自身血不变。当一方血量减为时,对方获胜。问获胜的概率。答案对取模。
解题思路
我们抽象成一个二维平面图,左下角,初始位于,然后有三个移动方向,问移动到的概率是多少(是任意一个不大于的数)。
当前位置为
-
移动到的概率
-
移动到的概率
-
移动到的概率
值得注意的是,从移动到的概率是
所以我们先计算移动到的概率,最后再计算移动到的概率。
这里有两个自由变量。
如果我们假设移动到,或者说,水平方向进行了次移动,还要假设,我们进行了次情况一的移动,则情况二进行了次,情况三进行了次移动。
则移动到的概率为
很显然这式子整不动。通常处理方法就是交换求和顺序。我们从实际意义来说明。
我们先假设进行了次情况一的移动,则情况二进行了次,情况三进行了次移动,其中。
则移动到的概率为
而后面这一项可以事先预处理一个前缀和
这样,最终的答案就是
值得注意的是在计算时,万万不可算出然后除以除以(取模意义上)。
因为当的时候,这样算的话。
而实际上我们应该认为,也就是说应当采用快速幂计算 (虽然会多个log)。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const LL mo = 998244353; const int N = 2e5 + 8; int n, m; LL jie[N], invjie[N]; LL sum[N]; LL p, q; LL a, b, c, d; LL inv100 = 828542813; LL ans; LL qpower(LL a, LL b) { LL qwq = 1; while (b) { if (b & 1) qwq = qwq * a % mo; b >>= 1; a = a * a % mo; } return qwq; } LL inv(LL x) { return qpower(x, mo - 2); } LL C(int n, int m) { if (n < m) return 0; return jie[n] * invjie[m] % mo * invjie[n - m] % mo; } int main(void) { int kase; read(kase); jie[0] = invjie[0] = 1; for (int i = 1; i < N; ++i) { jie[i] = jie[i - 1] * i % mo; invjie[i] = inv(jie[i]); } for (int ii = 1; ii <= kase; ii++) { read(n); read(m); read(p); read(q); p = p * inv100 % mo; q = q * inv100 % mo; d = inv((1 - (1 - p) * (1 - q) % mo + mo) % mo); a = p * q % mo * d % mo; b = p * ((1 - q + mo) % mo) % mo * d % mo; c = q * ((1 - p + mo) % mo) % mo * d % mo; sum[0] = 1; LL tmp = c; for (int i = 1; i < n; ++i) { sum[i] = (sum[i - 1] + tmp * C(m - 1 + i, i) % mo) % mo; tmp = tmp * c % mo; } ans = 0; LL qaq = 1; // LL qbq = qpower(b, m - 1); // LL invb = inv(b); int up = min(m - 1, n - 1); for (int i = 0; i <= up; ++i) { ans = (ans + qaq * qpower(b, m - i - 1) % mo * C(m - 1, i) % mo * sum[n - i - 1] % mo) % mo; // ans = (ans + qaq * qbq % mo * C(m - 1, i) % mo * sum[n - i - 1] % mo) % mo; // 0^0 = 1 qaq = qaq * a % mo; // qbq = qbq * invb % mo; } ans = ans * p % mo * d % mo; write(ans, '\n'); } return 0; }
F. Query on the Tree (Hdu 6843)
题目大意
qwq
解题思路
qwq
神奇的代码
qwq
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/13473041.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步