本次过题目c++标准为c++17
A. 选择
题目
输入文件: standard input
输出文件: standard output
时间限制: 1 second
内存限制: 256 megabytes
选择正确的道路出发吧。
—— 来自虚空的声音
给定一个初始序列 {1, 2, . . . , n},你需要进行若干次操作,将整个序列所有数变成零。每个操作由三步
组成:
- 选择一个下标集合 S = {i1, i2, . . . , ik} ⊆ {1, 2, . . . , n};
- 选择一个非负整数 x;
- 对每个选中的数减去 x:∀i ∈ S, ai ← ai − x。
请计算在最优策略下需要的操作次数。
输入
输入一行一个正整数 n (1 ≤ n ≤ 10^6)。
输出
输出一行一个正整数,表示最少需要的操作次数。
注释
对于第三组样例,一种最优方案的两次操作如下:
1. S = {1, 3}, x = 1 : a = {0, 2, 2};
2. S = {2, 3}, x = 2 : a = {0, 0, 0}.
可以证明,没有次数更少的方案。
题解
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int ans = 1;
while (n /= 2)
{
ans ++;
}
cout << ans;
return 0;
}
B. 锐评
题目
输入文件: standard input
输出文件: standard output
时间限制: 1 second
内存限制: 256 megabytes
如果人们在网上看到一家店的好评率太低,或者读到类似「厨子偷吃」的差评,可能就不会去吃这家饭店了。
不过,如果你是火锅店老板,你也许会想尽办法让自己好评如潮。以下是两个可能的操作:
- 花 1 块钱,买水军增加一条好评。
- 花 x 块钱,用类似「侵犯名誉权」的理由举报掉一条差评。如果没有差评,你不能这么做。
假设你有 m 块钱,现在网上有 a 条好评,b 条差评。好评率被定义为a / (a + b)
在预算范围内,你会想办法最大化好评率。请计算你最终能的得到的最高好评率。
由于你的动作真的很快,所以我们假定除了你的操作以外,评价数量不会发生变化。
输入
输入第一行一个整数 T (1 ≤ T ≤ 10^5),表示数据组数。
对于每组数据,输入一行四个整数 m, x, a, b (1 ≤ m, x, a, b ≤ 10^9)。
输出
对于每组数据,输出一行一个小数,表示答案。
如果你的答案在相对误差或者绝对误差 10^−6 以内,将会被认为是正确的。
题解
#include <iostream>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
long long m, x, a, b;
cin >> m >> x >> a >> b;
long double t1 = (long double)(a + m) / (long double)(a + b + m);
long double t2 = (long double)(a + m % x) / (long double)(max(0LL, b - m / x) + a + m % x);
printf("%0.9LF\n", max(t1, t2));
}
return 0;
}
C. 饮茶
题目
输入文件: standard input
输出文件: standard output
时间限制: 1 second
内存限制: 256 megabytes
活是干不完的,不如饮茶放工。
话虽然这么说,但是放工太久容易导致挂科。接下来 n 天,你每天都有一个作业在晚上 23:59 截止,做这个作业需要恰好 ti 小时。如果认真学习,那么你可以在第 i 天干 ai 小时的活;如果你决定开摆,和同学去饮茶、逛街、吃火锅,那么只能干 bi 小时的活。
为了避免老师捞不动,你决定还是把每个作业按时提交。你可以提前做作业:你可以在每个作业截止前的任意一个工作时间段做任意久该作业。但是,每个作业在截止前必须做完。
劳逸结合很重要!所以你想知道你最多有几天能出去玩。
输入
输入第一行一个正整数 n (1 ≤ n ≤ 10^5)。
接下来 n 行,每行三个整数 ti, ai, bi (0 ≤ ti ≤ 1000, 0 ≤ ai
, bi ≤ 24, ai ≥ bi)。
输出
输入一行一个整数,表示:
• 如果你再怎么努力都已经来不及了,输出 −1。
• 否则输出你最多有几天能出去玩。
注释
样例 1 解释:人有多大胆,地有多大产。
样例 2 解释:完蛋咯,建议直接开摆。
题解
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e5 + 5;
int t[N], a[N], b[N];
priority_queue<int> q;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, totalt = 0, now = 0, ans = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> t[i] >> a[i] >> b[i];
q.push(a[i] - b[i]);
ans++;
totalt += t[i] - b[i];
while (!q.empty() && now < totalt) {
now += q.top();
q.pop();
ans--;
}
if (now < totalt) {
cout << -1 << '\n';
return 0;
}
}
cout << ans << '\n';
return 0;
}
D. 脚本
题目
输入文件: standard input
输出文件: standard output
时间限制: 3 seconds
内存限制: 1024 megabytes
你最后还是决定饮茶放工,出去和队友吃火锅。不过在此之前火锅店老板已经对评论区进行了一次公关,你已经看不到多少中肯的评价了。同时,你也在评论区发现了一些端倪:因为老板请了水军,评论区的评论呈现出有一些有趣的特点。例如每一条评论的开头都离不开某几句话,因此你用一个小写字母(‘a’ - ‘z’) 来表示一种评论。
你还发现评论都是用比较拙劣的自动化工具刷的,因为不仅只有几种评论,依次查看评论甚至发现了循环节。根据你多年的脚本经验,这个脚本里面有一个长度为 p 的评论列表,这个脚本会按顺序循环往复地发布这 p 条评论。如果我们用字符串 s = s1s2 . . . s|s| 表示一系列连续的评论,那么我们说 p 是 s 的
循环节,当且仅当:
• p ≤ |s|;
• ∀1 ≤ i ≤ |s| − p, si = si+p.
你现在爬取了评论区所有的 n 条评论,你很好奇老板刷评论的脚本里面的评论列表长什么样。不过,在老板操作以前可能已经存在一些评论了,你不知道老板是从哪一条评论开始用脚本刷评论的,因此你想知道每一个后缀的周期性。具体而言,对于 i = 1 . . . n,假设最后的 i 条评论是用脚本刷的,你需要求
出脚本中所有可能循环节的大小(也即评论列表长度)之和ai:
输入
输入包含多组数据。第一行包含一个整数 T,代表数据组数。
每组数据的第一行包含一个正整数 n (1 ≤ n ≤ 106),代表评论区评论的数量;第二行包含一个仅包含小写字母的字符串 s。
保证所有数据的 n 总和不超过 106。
输出
对每组数据,在一行内输出 n 个用空格分隔的整数 a1, a2, . . . , an。
注释
对于第一组数据而言:
• 当 i = 1 时,评论列表只有可能为 a。
• 当 i = 2 时,评论列表只有可能为 ba。
• 当 i = 3 时,评论列表可能为 aba 或者 ab。
• 当 i = 4 时,评论列表可能为 baba 或者 ba。
• 当 i = 5 时,评论列表可能为 ababa, abab 或者 ab。
题解
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e6 + 5;
int n, ans[N], sum[N], nex[N];
char t[N];
void KMP(const char p[], int m) {
for (int i = 2, j = 0; i <= m; i++) {
while (j && p[i] != p[j + 1]) j = nex[j];
if (p[i] == p[j + 1]) j++;
nex[i] = j;
}
}
signed main() {
int T;
cin >> T;
while (T--) {
cin >> n >> (t + 1);
reverse(t + 1, t + 1 + n);
KMP(t, n);
for (int i = 1; i <= n; i++) {
int j = i - nex[i];
sum[i] = sum[nex[i]] + 1;
ans[i] = ans[nex[i]] + j * sum[i];
cout << ans[i] << " \n"[i == n];
}
}
return 0;
}
E2. 火锅(困难)
题目
输入文件: standard input
输出文件: standard output
时间限制: 2 seconds
内存限制: 512 megabytes
请注意,本题与(简单)的差别仅在 m 的范围上,其余内容一致。
你和你的队友换了一家评论区未被公关且好评如潮的火锅店吃火锅。
在涮菜的时候,你发现由于下锅时间不一致,而捞出锅时你不一定能捞到你下的菜(也有可能被你急急急的队友给捞走了),你既有可能吃到夹生的鸭肠,也有可能吃到煮久到嚼不动的鸭肠。
我们现在假定,m 种菜品中,菜品 i 需要花 li 秒煮熟,而如果 ri 秒后还没有捞出锅就煮老了;只有在下锅后 li 秒到 ri 秒内捞出的才是恰到好处的。
为了检查你们的涮菜操作是否是合理的,你记录下了完整的操作序列:
• add ti
: 在第 ti 秒下锅了一份菜,这份菜是从 m 种中均匀随机地选取的。
• pop ti
: 在第 ti 秒从锅中均匀随机地捞起了一份菜。进行该项操作时,锅中一定还有菜品。
你想知道,期望下你能吃到多少份恰到好处的菜。
输入
输入第一行包含两个正整数 m (1 ≤ m ≤ 202 305), n (1 ≤ n ≤ 202 305)。
接下来 m 行,每行两个整数 li, ri(1 ≤ li ≤ ri ≤ 202 305),表示一种菜品的好区间。
接下来 n 行,每行为以下两种中的一种:
• add ti
• pop ti
其中,1 ≤ ti ≤ 202 305,且 ti 互不相同,并且随着 i 单调增。
输出
输出一行一个整数,表示期望下你能吃到恰到好处的菜的数量对 998 244 353 取模的结果。
对答案取模的定义如下:可以证明,答案一定能表示成有理数 p/q,其中 p, q 为一对互素的整数(特别地,p 等于 0 时 q 为 1)。此时,你需要输出 p × q^−1 mod 998 244 353,可以证明这个答案唯一。
题解(参考大佬的答案)
#include <bits/stdc++.h>
#define int long long
#define fp(i, a, b) for (int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define fd(i, a, b) for (int i = (a), i##_ = (b) - 1; i > i##_; --i)
using namespace std;
const int maxn = 3e5 + 5, P = 998244353;
using ll = int64_t;
#define ADD(a, b) (((a) += (b)) >= P ? (a) -=P : 0) // (a += b) %= P
#define SUB(a, b) (((a) -= (b)) < 0 ? (a) += P: 0) // ((a -= b) += P) %= P
#define MUL(a, b) ((ll) (a) * (b) % P)
int POW(ll a, int b = P - 2, ll x = 1) {
for (; b; b >>= 1, a = a * a % P) if (b & 1) x = x * a % P;
return x;
}
namespace NTT {
const int g = 3;
vector<int> Omega(int L) {
int wn = POW(g, P / L);
vector<int> w(L);
w[L >> 1] = 1;
fp(i, L / 2 + 1, L - 1) w[i] = MUL(w[i - 1], wn);
fd(i, L / 2 - 1, 1) w[i] = w[i << 1];
return w;
}
auto W = Omega(1 << 21); // NOLINT
void DIF(int *a, int n) {
for (int k = n >> 1; k; k >>= 1)
for (int i = 0, y; i < n; i += k << 1)
fp(j, 0, k - 1)y = a[i + j + k], a[i + j + k] = MUL(a[i + j] - y + P, W[k + j]), ADD(a[i + j], y);
}
void IDIT(int *a, int n) {
for (int k = 1; k < n; k <<= 1)
for (int i = 0, x, y; i < n; i += k << 1)
fp(j, 0, k - 1)
x = a[i + j], y = MUL(a[i + j + k], W[k + j]),
a[i + j + k] = x - y < 0 ? x - y + P : x - y, ADD(a[i + j], y);
int Inv = P - (P - 1) / n;
fp(i, 0, n - 1) a[i] = MUL(a[i], Inv);
reverse(a + 1, a + n);
}
}
namespace Polynomial {
using Poly = std::vector<int>;
// mul/div int
Poly &operator*=(Poly &a, int b) {
for (auto &x: a) x = MUL(x, b);
return a;
}
Poly operator*(Poly a, int b) { return a *= b; }
Poly operator*(int a, Poly b) { return std::move(b) * a; }
Poly &operator/=(Poly &a, int b) { return a *= POW(b); }
Poly operator/(Poly a, int b) { return a /= b; }
// Poly add/sub
Poly &operator+=(Poly &a, Poly b) {
a.resize(max(a.size(), b.size()));
fp(i, 0, b.size() - 1) ADD(a[i], b[i]);
return a;
}
Poly operator+(Poly a, Poly b) { return a += std::move(b); }
Poly &operator-=(Poly &a, Poly b) {
a.resize(max(a.size(), b.size()));
fp(i, 0, b.size() - 1) SUB(a[i], b[i]);
return a;
}
Poly operator-(Poly a, Poly b) { return a -= std::move(b); }
// Poly mul
void DFT(Poly &a) { NTT::DIF(a.data(), a.size()); }
void IDFT(Poly &a) { NTT::IDIT(a.data(), a.size()); }
int norm(int n) { return 1 << (32 - __builtin_clz(n - 1)); }
void norm(Poly &a) { if (!a.empty()) a.resize(norm(a.size()), 0); }
Poly &dot(Poly &a, Poly &b) {
fp(i, 0, a.size() - 1) a[i] = MUL(a[i], b[i]);
return a;
}
Poly operator*(Poly a, Poly b) {
int n = a.size() + b.size() - 1, L = norm(n);
if (a.size() <= 8 || b.size() <= 8) {
Poly c(n);
fp(i, 0, a.size() - 1) fp(j, 0, b.size() - 1)c[i + j] = (c[i + j] + (ll) a[i] * b[j]) % P;
return c;
}
a.resize(L), b.resize(L);
DFT(a), DFT(b), dot(a, b), IDFT(a);
return a.resize(n), a;
}
// Poly inv
Poly Inv2k(Poly a) { // a.size() = 2^k
int n = a.size(), m = n >> 1;
if (n == 1) return {POW(a[0])};
Poly b = Inv2k(Poly(a.begin(), a.begin() + m)), c = b;
b.resize(n), DFT(a), DFT(b), dot(a, b), IDFT(a);
fp(i, 0, n - 1) a[i] = i < m ? 0 : P - a[i];
DFT(a), dot(a, b), IDFT(a);
return move(c.begin(), c.end(), a.begin()), a;
}
Poly Inv(Poly a) {
int n = a.size();
norm(a), a = Inv2k(a);
return a.resize(n), a;
}
// Poly div/mod
Poly operator/(Poly a, Poly b) {
int k = a.size() - b.size() + 1;
if (k < 0) return {0};
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
b.resize(k), a = a * Inv(b);
a.resize(k), reverse(a.begin(), a.end());
return a;
}
pair<Poly, Poly> operator%(Poly a, const Poly &b) {
Poly c = a / b;
a -= b * c, a.resize(b.size() - 1);
return {c, a};
}
}
const int mod = 998244353;
int ksm(int x, int k) {
int res = 1;
while (k) {
if (k & 1)res = res * x % mod;
x = x * x % mod;
k /= 2;
}
return res;
}
int ny(int x) {
return ksm(x, mod - 2);
}
void add(int &x, int y) {
if ((x += y) >= mod)x -= mod;
}
using namespace Polynomial;
string s[maxn];
int l[maxn], r[maxn];
int cf[maxn], sum[maxn];
Poly gg;
void suregg(int len) {
while ((int) gg.size() < len) {
gg.push_back(gg.back() + cf[(int) gg.size()]);
}
}
int caneat[maxn];
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
gg.push_back(0);
int m, n;
cin >> m >> n;
for (int i = 1; i <= m; i++)cin >> l[i] >> r[i], cf[l[i]]++, cf[r[i] + 1]--;
int all = 210000;
for (int i = 1; i <= n; i++) {
string ss;
int x;
cin >> ss >> x;
s[x] = ss;
}
int now = 0;
int pr = 0;
vector<pair<int, int>> Intval;
for (int i = 1; i <= all; i++) {
if (s[i] == "add") {
now++;
} else if (s[i] == "pop") {
now--;
if (now == 0) {
Intval.emplace_back(pr + 1, i);
pr = i;
}
}
}
if (pr != all)Intval.emplace_back(pr + 1, all);
sort(Intval.begin(), Intval.end(), [&](pair<int, int> p1, pair<int, int> p2) {
return p1.second - p1.first < p2.second - p2.first;
});
int ans = 0;
function<void(int, int)> solve = [&](int l, int r) {
int len = r - l + 1;
suregg(len);
for (int i = 0; i <= len; i++)sum[i] = 1, caneat[i] = 0;
Poly a(len + 1, 0);
int now = 0;
for (int i = l; i <= r; i++) {
if (s[i] == "add")now++;
else {
if (s[i] == "pop") {
caneat[i - l + 1] = ny(now) * sum[i - 1 - l + 1] % mod;
sum[i - l + 1] = (1 - ny(now) + mod) % mod;
now--;
}
}
sum[i - l + 1] = sum[i - l + 1] * sum[i - l] % mod;
}
for (int i = l; i <= r; i++)if (s[i] == "add")a[i - l + 1] = ny(sum[i - l]);
a = a * gg;
for (int i = 1; i <= len; i++) {
add(ans, a[i] * caneat[i] % mod);
}
};
for (auto [g, f]: Intval) {
solve(g, f);
}
cout << ans * ny(m) % mod << "\n";
return 0;
}