A.A Multiply(贪心)
题意:
给你一个长度为 NNN 、 A=(A1,A2,…,AN)A=(A_1,A_2,\dots,A_N)A=(A1,A2,…,AN) 和整数 CCC 的整数序列。
在进行最多一次以下操作后,求 AAA 中元素的最大可能和:
- 指定整数 lll 和 rrr ,并将 Al,Al+1,…,ArA_l,A_{l+1},\dots,A_rAl,Al+1,…,Ar 分别乘以 CCC 。
分析:
分类讨论:
-
C>0C>0C>0,求出最大子段和,乘上CCC,再加上原来非最大子段和的部分即可。
-
C≤0C \le 0C≤0,求出最小子段和,乘上CCC,再加上原来非最小子段和的部分即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5 + 5;
int a[N];
int n, c;
int main() {
cin >> n >> c;
for (int i = 1; i <= n; i++)
cin >> a[i];
if (c > 1) {
LL now = 0, ans = 0, sum = 0;
for (int i = 1; i <= n; i++) {
now = max(now, 0LL) + a[i];
ans = max(ans, now);
sum += a[i];
}
cout << sum + ans * (c - 1) << endl;
} else {
LL now = 0, ans = 0, sum = 0;
for (int i = 1; i <= n; i++) {
now = min(now, 0LL) + a[i];
ans = min(ans, now);
sum += a[i];
}
cout << sum + ans * (c - 1) << endl;
}
return 0;
}
B.Bought Review(贪心)
题意:
给出 TTT 个测试用例,解决以下问题。
在美食评论网站 EatCocoderEatCocoderEatCocoder 上,您可以用从 111 到 555 的整数颗星来评论餐馆。
最初,厨师 BBB 管理的餐厅有 AiA_iAi 条评论, iii 颗星。 (1≤i≤5)(1 \le i \le 5)(1≤i≤5)
厨师可以向 EatCocoderEatCocoderEatCocoder 管理部门支付 PiP_iPi 日元的贿赂,以获得额外的 iii 星级评论。 (1≤i≤5)(1 \le i \le 5)(1≤i≤5)
通过贿赂增加 kkk 条评论后,总共会有 A1+A2+A3+A4+A5+kA_1+A_2+A_3+A_4+A_5+kA1+A2+A3+A4+A5+k 条评论。
厨师 BBB 希望这些评论的平均分至少为 333 星。请确定实现这一目标所需的最低贿赂总额。
分析:
观察发现只有A4,A5A_4,A_5A4,A5对答案有贡献,只需要比较2×A42 \times A_42×A4和A5A_5A5的大小即可。之后贪心地进行选择,分三种情况:111.全选A4A_4A4,222.全选A5A_5A5,333.选一个A4A_4A4,其他的选A5A_5A5
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a[6], p[6];
int main() {
int t;
cin >> t;
while (t--) {
for (int i = 1; i <= 5; i++)
cin >> a[i];
for (int i = 1; i <= 5; i++)
cin >> p[i];
int res = a[1] + a[1] + a[2] - a[4] - a[5] - a[5];
if (res <= 0)
cout << 0 << endl;
else if (p[4] + p[4] <= p[5])
cout << p[4] * res << endl;
else if (p[5] <= p[4])
cout << (res + 1) / 2 * p[5] << endl;
else
cout << res / 2 * p[5] + res % 2 * p[4] << endl;
}
return 0;
}
C.Catastrophic Roulette(期望 dp)
题意:
问题陈述
有一个轮盘,它能以相等的概率从 1,2,…,N1,2,\dots,N1,2,…,N 中产生一个整数。
两个玩家用它玩下面的游戏:
-
玩家轮流转动轮盘。
-
如果产生的整数之前没有出现过,则不会发生任何事情。
-
否则,转动轮盘的玩家要支付 111 日元的罚款。
-
-
当所有 NNN 个整数都至少出现过一次时,游戏立即结束。
请分别求出第一位和第二位玩家在游戏结束前支付的罚款金额的期望值,取模 998244353998244353998244353 。
分析:
fi,gif_i,g_ifi,gi表示已经抽了iii个数,当前是 AliceAliceAlice 或 BobBobBob 抽的,期望罚款。
期望dpdpdp进行倒推处理,fn=gn=0f_n=g_n=0fn=gn=0,p=inp=\frac{i}{n}p=ni表示抽到已经有的概率。那么有转移方程:
fi=(1−p)×gi+1+p×gif_i=(1-p) \times g_{i+1} + p \times g_ifi=(1−p)×gi+1+p×gi
gi=(1−p)×fi+1+p×(fi+1)g_i=(1-p) \times f_{i+1} + p \times (f_i+1)gi=(1−p)×fi+1+p×(fi+1)
将fif_ifi带入gig_igi,并通过移项得到:
fi=(1−p)×gi+1+p×(1−p)×fi+1+p21−p2f_i = \frac{(1-p) \times g_{i+1}+p \times (1-p) \times f_{i+1} + p^2}{1-p^2}fi=1−p2(1−p)×gi+1+p×(1−p)×fi+1+p2
求得gig_igi带入求fif_ifi即可。答案为f1,g1f_1,g_1f1,g1。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5;
const int mod = 998244353;
LL f[N], g[N];
LL inv(LL x, LL y = mod - 2) {
x = (x % mod + mod) % mod;
LL ans = 1;
while (y) {
if (y & 1)
ans = 1ll * ans * x % mod;
x = 1ll * x * x % mod;
y >>= 1;
}
return ans;
}
int main() {
int n;
cin >> n;
for (int i = n - 1, p; i; i--)
p = 1ll * i * inv(n) % mod,
f[i] = ((g[i + 1] * (1ll - p) + 1ll * f[i + 1] * p % mod * (1ll - p) + 1ll * p * p) % mod *
inv(1ll - 1ll * p * p) % mod + mod) % mod,
g[i] = ((f[i + 1] * (1ll - p) + (f[i] + 1ll) * p) % mod + mod) % mod;
cout << f[1] << ' ' << g[1] << endl;
return 0;
}
D.Digit vs Square Root (打表)
题意:
为 TTT 个测试用例解决以下问题。
给定整数 NNN ,求满足以下所有条件的整数 xxx 的个数:
- 1≤x≤N1 \le x \le N1≤x≤N
- 让 y=⌊x⌋y = \lfloor \sqrt{x} \rfloory=⌊x⌋ .将 xxx 和 yyy 用十进制表示(不带前导零), yyy 是 xxx 的前缀。
分析:
通过打表发现,若xxx满足条件,那么xxx是以下两种形式之一:
- x=100k−2×10kx=100^k-2 \times 10^kx=100k−2×10k
- x∈[100k−10k,100k+10k)x \in [100^k-10^k,100^k+10^k)x∈[100k−10k,100k+10k)
枚举kkk,计算区间交集即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
int main() {
int t;
cin >> t;
while (t--) {
LL n;
cin >> n;
LL ans = 1;
for (LL i = 10; i <= 1000000000; i *= 10) {
LL tmp = i * i;
if (n >= tmp - i - i)
ans++;
if (n >= tmp - i)
ans += min(tmp + i - 1, n) - tmp + i + 1;
}
cout << ans << endl;
}
return 0;
}
E.Existence Counting (树状数组)
题意:
给你整数 NNN 和 KKK 。考虑长度为 KKK 的序列 a=(a1,a2,…,aK)a=(a_1,a_2,\dots,a_K)a=(a1,a2,…,aK) ,它满足以下所有条件:
-
aia_iai 是一个整数,使得 1≤ai≤N1 \le a_i \le N1≤ai≤N .
-
aaa 中的所有元素都不同。
让我们把所有可能的序列 aaa 按词典顺序排列,形成一个 “序列的序列”,称为字典 sss 。
给定一个存在于字典 sss 中的序列 PPP ,针对每个整数 t=1,2,…,Nt=1,2,\dots,Nt=1,2,…,N 回答下面的问题:
-
求满足以下所有条件的序列 bbb 的个数(模为 998244353998244353998244353 ):
-
字典 sss 中存在序列 bbb 。
-
整数 ttt 包含在序列 bbb 中。
-
序列 bbb 在词法上小于或等于序列 PPP 。
-
分析:
首先不考虑必须出现某个数ttt的限制,只计算PPP之前有多少排列。和计算排列的时候一样,我们从前往后枚举每个位置iii,通过树状数组计算这个位置上能填多少个比现在填的数PiP_iPi小的数,再乘上后面的填法数,填法数为(n−i)!(n−k)!\frac{(n-i)!}{(n-k)!}(n−k)!(n−i)!。那么PPP之前有∑i=1k(n−i)!(n−k)!(Pi−1−∑i=1j[Pj<Pi])\sum\limits_{i=1}^{k}\frac{(n-i)!}{(n-k)!}(P_i-1-\sum_{i=1}^{j}[P_j < P_i])i=1∑k(n−k)!(n−i)!(Pi−1−∑i=1j[Pj<Pi])个序列。
再考虑ttt不出现的序列有多少个。实际上就相当于在序列的值域[1,n][1,n][1,n]中直接去掉了ttt这个数,那等价于直接把值域当成 [1,n−1][1,n−1][1,n−1],然后把所有Pi>tP_i>tPi>t减111。这样,在[1,n−1][1,n−1][1,n−1]中,修改过的 PPP之前出现的序列,就和不含ttt的序列对应。用上面的式子计数即可。
其中Pi=tP_i=tPi=t的情况需要特殊考虑。由于这个PiP_iPi实际上在值域中并不存在,而是一个比[1,t−1][1,t−1][1,t−1]大比[t+1,n][t+1,n][t+1,n]小的神秘数,所以iii后面的所有数都不会造成贡献,需要用另一个树状数组维护每个位置 iii造成的贡献,然后减掉后面不会造成贡献的一段。另外我们统计的是PPP之前的序列有多少个含ttt,所求的范围是包含PPP本身的,所以此时还需要给答案额外加一。
对于nnn个询问,我们从小到大处理每个询问,一开始每个PiP_iPi都有Pi>tP_i>tPi>t,所以都要减掉111。处理询问的时候每次碰到有Pi=tP_i=tPi=t就把111加回来。体现在答案上就是减去(n−i)!(n−k)!\frac{(n-i)!}{(n-k)!}(n−k)!(n−i)!
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n, k;
int p[N], c[N], d[N], q[N];
const int mod = 998244353;
int tmp[N], invf[N];
int binpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1)
ans = 1LL * ans * x % mod;
x = 1LL * x * x % mod;
y >>= 1;
}
return ans;
}
int cal(int x, int y) {
return 1LL * tmp[x] * invf[x - y] % mod;
}
void add(int x) {
for (; x <= n; x += (x & -x))
c[x]++;
}
int query(int x) {
int ans = 0;
for (; x; x -= (x & -x))
ans += c[x];
return ans;
}
void change(int x, int val) {
for (; x <= k; x += (x & -x))
d[x] = (d[x] + val) % mod;
}
int ask(int x) {
int ans = 0;
for (; x; x -= (x & -x))
ans = (ans + d[x]) % mod;
return ans;
}
int main() {
cin >> n >> k;
tmp[0] = 1;
for (int i = 1; i <= n; i++)
tmp[i] = 1LL * tmp[i - 1] * i % mod;
invf[n] = binpow(tmp[n], mod - 2);
for (int i = n; i >= 1; i--)
invf[i - 1] = 1LL * invf[i] * i % mod;
for (int i = 1; i <= k; i++)
cin >> p[i];
for (int i = 1; i <= k; i++)
q[p[i]] = i;
int tot = 0;
for (int i = 1; i <= k; i++) {
tot = (tot + 1LL * (p[i] - 1 - query(p[i] - 1)) * cal(n - i, k - i)) % mod;
add(p[i]);
}
if (n == k) {
for (int i = 1; i <= n; i++)
cout << tot + 1 << endl;
return 0;
}
int sub = 0;
for (int i = 1; i <= n; i++)
c[i] = 0;
for (int i = 1; i <= k; i++) {
sub = (sub + 1LL * (p[i] - 2 - query(p[i] - 1)) * cal(n - 1 - i, k - i)) % mod;
change(i, 1LL * (p[i] - 2 - query(p[i] - 1)) * cal(n - 1 - i, k - i) % mod);
add(p[i]);
}
for (int i = 1; i <= n; i++) {
if (q[i])
sub = (sub + cal(n - 1 - q[i], k - q[i])) % mod;
int ans = (tot - sub + mod) % mod;
if (q[i])
ans = (ans + ask(k) - ask(q[i]) + 1) % mod;
if (q[i])
change(q[i], cal(n - 1 - q[i], k - q[i]));
cout << (ans + mod) % mod << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

92

被折叠的 条评论
为什么被折叠?



