AtCoder Beginner Contest 463 题解
AtCoder Beginner Contest 463 题解
| 项目 | 内容 |
|---|---|
| 比赛时间 | 2026-06-20 |
| 比赛链接 | https://atcoder.jp/contests/abc463 |
| 题目数量 | A–G 共 7 题 |
比赛概览
本场整体难度中等偏上。A、B 为简单的签到题;C 题考察"剔除无用信息 + 二分"的观察力;D 题是经典的"二分答案 + 区间调度"贪心;E 题是构造超级源/汇结点后跑 Dijkstra 的经典套路;F 题是一道较难的组合概率题,需要对"冠军候选者"做细致的分类讨论,并配合组合数求和;G 题则是随机游走期望与莫队(Mo's algorithm)的结合,是本场区分度最高、最值得掌握的题目。重点建议掌握 C、D、E 的套路,以及 F、G 的分析方法。
A - 16:9
题目链接:https://atcoder.jp/contests/abc463/tasks/abc463_a
题目大意
给定图片的宽 与高 (,均为整数),判断宽高比是否为 ,即 。若成立输出 Yes,否则输出 No。
思路
由比例的性质, 等价于
交叉相乘去分母,得到
注意:
- 直接使用浮点数判断 可能引入精度误差;两边同时乘以 后化为整数比较则完全精确。
- 由于 , 与 最大不超过 ,用
int或long long均可。
因此只需判断 9 * X == 16 * Y 即可。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
long long X, Y;
cin >> X >> Y;
cout << (9 * X == 16 * Y ? "Yes" : "No") << '\n';
return 0;
}官方题解同时给出了等价的核心代码:
X, Y = map(int, input().split())
if X * 9 == Y * 16:
print("Yes")
else:
print("No")复杂度分析
- 时间复杂度:
- 空间复杂度:
B - Train Reservation
题目链接:https://atcoder.jp/contests/abc463/tasks/abc463_b
题目大意
有 列列车,每列有 A、B、C、D、E 五个座位列。给出每列列车的座位空缺情况字符串 (长度为 ,由 o/x 组成,第 个字符分别对应 AE 列)。给定目标座位列 ,判断是否存在至少一列列车在 列有空位。若存在输出 Yes,否则输出 No。
思路
逐一检查每列列车即可。将列名 转化为下标:
也就是 。对每行字符串 ,只需判断 S_i[idx] == 'o'。一旦找到任何一个 o,答案即为 Yes。
这是最朴素的线性扫描, 完全够用。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int N;
char X;
cin >> N >> X;
int idx = X - 'A';
bool ok = false;
for (int i = 0; i < N; i++) {
string s;
cin >> s;
if (s[idx] == 'o') ok = true;
}
cout << (ok ? "Yes" : "No") << '\n';
return 0;
}官方题解给出的 Python 参考代码:
N, X = input().split()
N = int(N)
X = ord(X) - ord('A')
S = [input() for _ in range(N)]
ans = False
for s in S:
ans |= s[X] == "o"
if ans:
print("Yes")
else:
print("No")复杂度分析
- 时间复杂度:
- 空间复杂度:(不计输入存储,或 若存储全部字符串)
C - Tallest at the Moment
题目链接:https://atcoder.jp/contests/abc463/tasks/abc463_c
题目大意
会议室中有 个人,第 个人身高为 ,将于 分钟后离开且不再回来,其中 。有 个询问,第 个询问给出整数 ,求 分钟后仍在会议室的人中的最高身高。题目保证每个询问时刻都至少有一人在会议室。
思路
关键观察(剔除无用的人)。 若两个人满足"一个更高、且离开得更晚",即存在 使得
那么在任意时刻,只要 还在会议室(), 也一定还在会议室(因为 ),并且 的身高不矮于 。因此 在任意时刻都不可能成为"最高的人",从会议室中把它"剔除"不会影响任何询问的答案。
由于输入已经按 非降序给出,我们可以用单调栈在线地完成剔除:
- 依次处理每个人 ;
- 只要栈顶元素的身高 (即栈顶那个人更矮、且离开得不比当前人晚,被当前人"支配"),就弹出它;
- 把 压入栈。
经过这一处理,栈中元素满足:身高严格递减,离开时间非递减。
回答询问。 注意 为整数,因此在 时刻第 个人仍在会议室当且仅当
由于栈中 非递减, 的人构成栈的一个后缀(即栈中第一个满足 的人及其后面的所有人)。又因为栈中身高严格递减,所以这个后缀的第一个元素——也就是 的人中最早进栈的那个——就是答案(它最高)。用二分(upper_bound 找第一个 的位置)即可 回答每个询问。
为什么可行? 弹出条件保证相邻元素身高严格递减;又因为输入按 非降序处理、且只会在栈顶压入,栈中 保持非递减。这两条性质正是回答询问所需要的: 非递减保证"仍在会议室"的人是栈的一个后缀(可二分定位),身高递减保证该后缀的第一个元素就是当前最高的人。
另一种等价视角(更直观的解法):因为 单调不减, 时刻仍在会议室的人恰好是原序列的一个后缀,先用后缀 预处理,再二分定位分界点。两种做法的本质相同,栈做法相当于预先压缩了序列。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
vector<pair<long long, long long>> st; // (H, L),处理后 H 严格递减、L 非递减
for (int i = 0; i < N; i++) {
long long H, L;
cin >> H >> L;
while (!st.empty() && st.back().first <= H) st.pop_back(); // 被支配的人先走
st.push_back({H, L});
}
vector<long long> Ls; // 只存 L,用于二分
for (auto &p : st) Ls.push_back(p.second);
int Q;
cin >> Q;
while (Q--) {
long long T;
cin >> T;
int j = upper_bound(Ls.begin(), Ls.end(), T) - Ls.begin(); // 第一个 L > T 的人
cout << st[j].first << '\n';
}
return 0;
}复杂度分析
- 时间复杂度:(构造栈 ,每个询问二分 )
- 空间复杂度:
D - Maximize the Gap
题目链接:https://atcoder.jp/contests/abc463/tasks/abc463_d
题目大意
数轴上有 块布,第 块覆盖闭区间 ()。两块布"重叠"当且仅当存在某个点同时被两者覆盖(在端点相交也算重叠)。对两块不重叠的布,定义它们的距离为
选出 块两两不重叠的布,其得分定义为所有两两距离中的最小值。求最大可能得分;若无法选出 块两两不重叠的布,输出 -1。
思路
问题转化。 设两块布 、 不重叠且 (即 在 左边),则距离为 。若把选出的布按右端点从左到右排好,则两两距离的最小值就等于相邻两块之间的距离的最小值(非相邻的间距只会更大)。因此问题变成:
能否选出 块布,使排序后相邻两块满足 ,即 ?
二分答案 。 可行性随 增大而单调变差,所以可以二分最大的 。
贪心判断可行性(区间调度)。 将所有布按右端点 升序排序,维护变量 last 表示上一块被选中布的右端点,初始为 。按顺序扫描:
- 若当前布满足 (即与前一块的间距至少为 ),就选中它并令
last = R_i; - 否则跳过。
记选中的块数为 cnt,则 cnt >= K 即可行。
贪心正确性。 这是经典区间调度贪心:总是选择"结束最早且可行的"区间。假设贪心选出的第一块是 ,任意一个可行方案选出的第一块是 。由于按 升序扫描,;把可行方案中的 替换成 ,后续区间只需要满足 ,而 ,所以替换后依然可行,选择个数不减少。归纳可知贪心得到的选择数最大。而"最大选择数 "正是"能选出 块间距 的布"的充要条件。
判定无解。 当 都不可行时(即连 块两两不重叠的布都选不出来),输出 -1。注意 意味着 ,即 ,这正是"严格不重叠"(在端点重合也算重叠,故必须严格大于)。
边界情况。 时只有全选一种可能,需检查这 块是否两两间距 ;二分框架天然覆盖。所有值使用 64 位整数。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, K;
cin >> N >> K;
vector<pair<long long, long long>> a(N);
for (int i = 0; i < N; i++) cin >> a[i].first >> a[i].second;
// 按右端点排序
sort(a.begin(), a.end(), [](auto &x, auto &y) { return x.second < y.second; });
// 判断能否选出 K 块,使得相邻间距 >= d
auto ok = [&](long long d) {
long long last = -(1LL << 60); // 上一块被选中布的右端点,初始为 -inf
int cnt = 0;
for (auto &[L, R] : a) {
if (last + d <= L) { // 与前一块的间距至少为 d
cnt++;
last = R;
}
}
return cnt >= K;
};
if (!ok(1)) { // 连 K 块互不重叠的布都选不出
cout << -1 << '\n';
return 0;
}
long long lo = 1, hi = 2000000000LL, ans = 1;
while (lo <= hi) {
long long mid = (lo + hi) / 2;
if (ok(mid)) ans = mid, lo = mid + 1;
else hi = mid - 1;
}
cout << ans << '\n';
return 0;
}复杂度分析
- 时间复杂度:,其中 为二分上界( 量级),即约
- 空间复杂度:
E - Roads and Gates
题目链接:https://atcoder.jp/contests/abc463/tasks/abc463_e
题目大意
有 个城市和 条双向道路,第 条道路连接 ,通行耗时 分钟。此外每个城市都装有传送门:从城市 传送到城市 需要 分钟( 为全局常量)。同一城市内换乘不花时间。对每个 ,求从城市 到城市 的最短时间。答案可能超过 。
思路
问题转化。 关键在于把"传送门"这种两点之间的任意连通改写成普通图的边。观察传送代价:
它可以拆成三段:(传送起点)花费 ,中间花费 ,(传送终点) 花费 。于是我们引入两个超级结点 和 :
- 对每个城市 ,加有向边 ,权值为 ;
- 加有向边 ,权值为 ;
- 加有向边 ,权值为 。
那么任意一次"从 传送到 "恰好对应路径 ,总代价恰为 。原题的所有旅行方式(道路或传送门)都变成新图上的有向/无向边。
注意 只有一条出边指向 , 只出边指向各城市,因此不会产生无意义的环路;多次使用传送门等价于多次经过 这条边,最短路径上不会重复绕圈。
求解。 新图共有 个结点、 条边,从城市 出发跑 Dijkstra 即可。由于边权为正,Dijkstra 是正确的。
注意: 最短路径可能达到 (样例 2 即如此),必须使用 64 位整数(long long)。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, M;
long long Y;
cin >> N >> M >> Y;
vector<vector<pair<int, long long>>> g(N + 2); // 0..N-1 为城市,N = W_S,N+1 = W_T
for (int i = 0; i < M; i++) {
int u, v;
long long T;
cin >> u >> v >> T;
--u; --v;
g[u].push_back({v, T});
g[v].push_back({u, T});
}
for (int i = 0; i < N; i++) {
long long X;
cin >> X;
g[i].push_back({N, X}); // 城市 i -> W_S,权值 X_i
g[N + 1].push_back({i, X}); // W_T -> 城市 i,权值 X_i
}
g[N].push_back({N + 1, Y}); // W_S -> W_T,权值 Y
const long long INF = (1LL << 62);
vector<long long> dist(N + 2, INF);
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<>> pq;
dist[0] = 0;
pq.push({0, 0});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) continue;
for (auto [v, w] : g[u]) {
if (dist[v] > d + w) {
dist[v] = d + w;
pq.push({dist[v], v});
}
}
}
for (int k = 1; k < N; k++) cout << dist[k] << (k == N - 1 ? '\n' : ' ');
return 0;
}复杂度分析
- 时间复杂度:
- 空间复杂度:
F - Senshuraku
题目链接:https://atcoder.jp/contests/abc463/tasks/abc463_f
题目大意
名选手即将进行最后的 场比赛,第 场由第 与第 名选手对阵,每场两人胜率各为 。比赛前选手 已获胜 场。全部结束后,从胜场最多的人中等概率随机选出一名冠军。对每名选手,求其成为冠军的概率,对 取模后输出。
思路
本题信息量大,关键在于细致的分类与组合计数。
观察 1:谁还有机会。 每名选手在最后 场中最多赢 场,因此最终胜场为 或 。设
则冠军的胜场只能是 或 。于是 的选手最终至多 胜,永远不可能成为候选者,可以把他们的胜场数当作 处理(不影响任何概率)。
观察 2:对局分类。 修改后每场对局双方的胜场数只可能是 中的值。把每场对局按双方胜场数排序成 ()后分为 6 类,数量记为 :
| 类别 | 双方胜场 | 含义 |
|---|---|---|
| 两名 胜选手对局 | ||
| 胜与 胜对局 | ||
| 胜与 胜对局 | ||
| 两名 胜对局 | ||
| 胜与 胜对局 | ||
| 两名 胜对局 |
统一框架。 固定"冠军胜场"为 。称最终胜场恰好等于 的人为候选者。对某一类对局,其结果要么产生若干候选者,要么导致"矛盾"(即某人的胜场超过了 ,使得最大胜场不可能是 )。逐类分析后,每类对局必属于下列两种之一:
- 类型一:以概率 产生恰好 名候选者,否则产生 名;
- 类型二:以概率 产生 名候选者(),以概率 矛盾。
设所有类型二对局的 之积为 、 之和为 ,类型一对局的数量为 。则在"不矛盾"的条件下,候选者总数为 加上类型一对局中"成功"的次数,取值范围是 。
- 对参与某场类型一对局的玩家:包含该玩家且候选者总数为 的概率为
推导:该玩家必须"成功"(概率 ,已经算在分母 中),其余 场类型一对局中恰有 场成功。
- 对参与某场类型二对局的玩家:包含该玩家且候选者总数为 的概率为
其中 是"在不矛盾的前提下该玩家恰好达到 胜"的条件概率。
得到上述概率后,该玩家成为冠军的概率就是把 对 求和。
下面分两种情况具体化。
情况一:冠军胜场为 。
此时候选者 = 初始 胜且赢下自己对局的玩家。
- 、 类( 胜方赢则达到 )是类型一对局,;
- 类: 对 ,无论谁赢都恰好产生 名候选者,属类型二,;
- 类:不可能有人达到 ,属类型二,。
所以 ,,候选者总数 。
- 类中的某玩家:成为候选者需赢下对局(概率 ),其余 场类型一对局独立地以 产生候选者,故
- 、 类中初始 胜的玩家:需赢下对局,其余 场类型一对局中有 场成功,故
情况二:冠军胜场为 。
此时若存在 类对局( 对 ),必产生一名 胜者,冠军胜场不可能为 ,故仅当 时考虑。
- 类是类型一对局,;
- 类: 方获胜时,该玩家达到 ,而 方保持 ,两人都成为候选者(,); 方获胜则产生 ,矛盾;
- 类: 方获胜则矛盾, 方获胜则 方保持 成为唯一候选者(,);
- 类:两名 选手对局,胜者达到 ,恰好 名候选者();
- 类:无候选者()。
于是
候选者总数 。
- 类中双方玩家:不矛盾时( 方获胜)两人都成为候选者,:
- 类中初始 胜的玩家:同理 ,公式同上;
- 类中的某玩家:需赢下对局,:
- 类中初始 胜的玩家:需赢下对局(),其余 场类型一对局中有 场成功:
实现要点。 预计算 的阶乘与阶乘逆元,即可 求组合数 、 以及 。两类情况下对 的循环总长度都是 。最后按每场对局把对应概率输出给双方。
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 998244353;
ll modpow(ll a, ll e) {
ll r = 1;
while (e) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
int M2 = 2 * N;
// 阶乘 / 阶乘逆元
vector<ll> fact(M2 + 1), ifact(M2 + 1);
fact[0] = 1;
for (int i = 1; i <= M2; i++) fact[i] = fact[i - 1] * i % MOD;
ifact[M2] = modpow(fact[M2], MOD - 2);
for (int i = M2; i >= 1; i--) ifact[i - 1] = ifact[i] * i % MOD;
auto C = [&](int n, int r) -> ll {
if (r < 0 || r > n) return 0;
return fact[n] * ifact[r] % MOD * ifact[n - r] % MOD;
};
auto inv = [&](int n) -> ll { // 1 / n
return ifact[n] * fact[n - 1] % MOD;
};
vector<pair<int, int>> match(N);
int W = 0;
for (int i = 0; i < N; i++) {
int a, b;
cin >> a >> b;
match[i] = {a, b};
W = max({W, a, b});
}
// 分类:0..5 对应 m_0..m_5
auto type = [&](int L, int R) -> int {
if (L > R) swap(L, R);
if (R == W) {
if (L == W) return 0;
if (L + 1 == W) return 1;
return 2;
}
if (R + 1 == W) {
if (L + 1 == W) return 3;
return 4;
}
return 5;
};
ll cnt[6] = {0, 0, 0, 0, 0, 0};
for (auto &[a, b] : match) cnt[type(a, b)]++;
ll inv2 = (MOD + 1) / 2;
ll prob[6][2] = {{0}}; // prob[t][0] 给胜场多者, prob[t][1] 给胜场少者
// 情况一:冠军胜场为 W+1
{
ll mn = cnt[0], mx = cnt[0] + cnt[1] + cnt[2];
ll coef = modpow(modpow(2, cnt[1] + cnt[2]), MOD - 2); // 1 / 2^(m1+m2)
if (mn) {
for (int w = mn; w <= mx; w++) {
ll p = C(cnt[1] + cnt[2], w - mn) * coef % MOD * inv(w) % MOD;
prob[0][0] = (prob[0][0] + p * inv2) % MOD;
prob[0][1] = (prob[0][1] + p * inv2) % MOD;
}
}
for (int w = mn + 1; w <= mx; w++) {
ll p = C(cnt[1] + cnt[2] - 1, w - mn - 1) * coef % MOD * inv(w) % MOD;
prob[1][0] = (prob[1][0] + p) % MOD; // W 胜方
prob[2][0] = (prob[2][0] + p) % MOD; // W 胜方
}
}
// 情况二:冠军胜场为 W(仅当 m_0 = 0 时可行)
if (cnt[0] == 0) {
ll mn = 2 * cnt[1] + cnt[2] + cnt[3];
ll mx = mn + cnt[4];
ll coef = modpow(modpow(2, cnt[1] + cnt[2] + cnt[4]), MOD - 2);
if (mn) {
for (int w = mn; w <= mx; w++) {
ll p = C(cnt[4], w - mn) * coef % MOD * inv(w) % MOD;
prob[1][0] = (prob[1][0] + p) % MOD;
prob[1][1] = (prob[1][1] + p) % MOD;
prob[2][0] = (prob[2][0] + p) % MOD;
prob[3][0] = (prob[3][0] + p * inv2) % MOD;
prob[3][1] = (prob[3][1] + p * inv2) % MOD;
}
}
for (int w = mn + 1; w <= mx; w++) {
ll p = C(cnt[4] - 1, w - mn - 1) * coef % MOD * inv(w) % MOD;
prob[4][0] = (prob[4][0] + p) % MOD;
}
}
for (int i = 0; i < N; i++) {
int L = match[i].first, R = match[i].second;
int t = type(L, R);
if (L < R) cout << prob[t][1] << " " << prob[t][0] << " ";
else cout << prob[t][0] << " " << prob[t][1] << " ";
}
cout << '\n';
return 0;
}复杂度分析
- 时间复杂度:(阶乘预计算 ,两类情况对 的求和总计 )
- 空间复杂度:
G - Random Walk Distance
题目链接:https://atcoder.jp/contests/abc463/tasks/abc463_g
题目大意
Takahashi 从数轴原点出发,进行 次移动:每次等概率地向左或向右移动 个单位,各次移动独立。设最终坐标为 。给定 组询问 ,求 对 取模的值。
思路
分布。 步后若向右走了 步,则坐标
且
由对称性 ,且 恒成立。
简单情形 。 若 ,则 ,所以 ,期望为
同理若 ,期望为 。
一般情形 。 记 。利用恒等式
其中 为指示函数,而条件 。于是
定义
则答案写为
快速计算 :莫队算法。 直接对每个询问求前缀和太慢;但 在二维网格 上相邻点之间可以 转移。利用组合恒等式 :
由后两式反解即可得到 减小、 减小的转移。约定 (当 ),则上述递推在"越界"的临时状态(如 )下依然成立。
把所有询问按 转为平面上的点 ,用莫队按" 所在块"排序,指针 在相邻询问间移动,每次移动 。取块大小 时,总移动次数
可以承受。
模运算。 预计算阶乘与阶乘逆元实现 组合数;最后把 乘以 (预计算 的负幂)并减 。注意 可为负,需要正确取模。
边界: 的询问不进入莫队,直接输出 。
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 998244353;
ll modpow(ll a, ll e) {
ll r = 1;
while (e) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
vector<pair<int, int>> queries(T); // (N, X)
int maxN = 0;
for (int i = 0; i < T; i++) {
int N, X;
cin >> N >> X;
queries[i] = {N, X};
maxN = max(maxN, N);
}
// 阶乘 / 阶乘逆元(用于 O(1) 组合数)
vector<ll> fact(maxN + 1), ifact(maxN + 1);
fact[0] = 1;
for (int i = 1; i <= maxN; i++) fact[i] = fact[i - 1] * i % MOD;
ifact[maxN] = modpow(fact[maxN], MOD - 2);
for (int i = maxN; i >= 1; i--) ifact[i - 1] = ifact[i] * i % MOD;
auto C = [&](int n, int k) -> ll {
if (n < 0 || k < 0 || k > n) return 0;
return fact[n] * ifact[k] % MOD * ifact[n - k] % MOD;
};
// 2 的幂与 2 的负幂
vector<ll> ipow2(maxN + 1);
ipow2[0] = 1;
ll inv2 = (MOD + 1) / 2;
for (int i = 1; i <= maxN; i++) ipow2[i] = ipow2[i - 1] * inv2 % MOD;
// 构造莫队询问
struct Q {
int n, m, id;
};
vector<Q> qs;
vector<ll> ans(T);
for (int i = 0; i < T; i++) {
int N = queries[i].first, X = queries[i].second;
if (X >= N) { ans[i] = X % MOD; continue; }
if (X <= -N) { ans[i] = (-(ll)X) % MOD; continue; }
int m = (N + X + 1) / 2; // ceil((N+X)/2)
qs.push_back({N, m, i});
}
int B = max(1, (int)(maxN / sqrt((double)max(1, T)) + 1));
sort(qs.begin(), qs.end(), [&](const Q &a, const Q &b) {
int ba = a.n / B, bb = b.n / B;
if (ba != bb) return ba < bb;
return (ba & 1) ? a.m > b.m : a.m < b.m;
});
// 当前状态 (n, m) 对应的 f, g
ll f = 0, g = 0;
int n = 0, m = 0;
auto incN = [&]() { // (n, m) -> (n+1, m)
ll c = C(n, m - 1);
ll nf = (2 * f - c) % MOD;
ll ng = (2 * g + f - (ll)m * c) % MOD;
if (nf < 0) nf += MOD;
if (ng < 0) ng += MOD;
f = nf; g = ng; ++n;
};
auto decN = [&]() { // (n, m) -> (n-1, m)
--n;
ll c = C(n, m - 1);
ll nf = (f + c) * inv2 % MOD;
ll ng = (g - nf + (ll)m * c) % MOD;
ng = (ng % MOD + MOD) % MOD * inv2 % MOD;
f = nf; g = ng;
};
auto incM = [&]() { // (n, m) -> (n, m+1)
ll c = C(n, m);
f = (f + c) % MOD;
g = (g + (ll)m * c) % MOD;
++m;
};
auto decM = [&]() { // (n, m) -> (n, m-1)
--m;
ll c = C(n, m);
f = (f - c) % MOD;
g = (g - (ll)m * c) % MOD;
if (f < 0) f += MOD;
if (g < 0) g += MOD;
};
for (auto &q : qs) {
while (n < q.n) incN();
while (n > q.n) decN();
while (m < q.m) incM();
while (m > q.m) decM();
int X = queries[q.id].second;
ll term = ((ll)(q.n + X) % MOD * f % MOD - 2 * g % MOD + MOD) % MOD;
ll xmod = (X % MOD + MOD) % MOD;
ans[q.id] = (term * ipow2[q.n - 1] % MOD - xmod + MOD) % MOD;
}
for (int i = 0; i < T; i++) cout << ans[i] << '\n';
return 0;
}复杂度分析
- 时间复杂度:,其中 (莫队总移动量)
- 空间复杂度: