专栏文章
题解:P13831 【MX-X18-T3】「FAOI-R6」比亚多西
P13831题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mio4twy6
- 此快照首次捕获于
- 2025/12/02 13:23 3 个月前
- 此快照最后确认于
- 2025/12/02 13:23 3 个月前
应该能感觉到 存在递推关系。打个表可以发现 。
证明
考虑题目中不断二分查找的过程可以用一棵 的二叉搜索树来刻画,每个结点的深度就是需要二分查找的次数。
例如对于 :

对于区间 的二分查找树:根节点是 ,左子树是 的二分查找树,右子树是 的二分查找树。
当区间从 扩展到 时,新插入的结点是 ,同时结点 的插入位置一定在树的最右侧路径上,深度为 ,即查找次数 (也是二分查找的最坏次数)。
所以 。证毕。
题目要求 的区间和,直接算不好算,可以考虑转化为前缀和相减的形式。设 ,则题目要求的 。
化简 可以考虑对每个 单独计算贡献,再加起来:
发现 单调不降,且每个值都会重复许多次,所以考虑算出每个值重复出现的次数,从而减少运算(类似整除分块)。
具体地,发现 对应 ,设 ,可得:
发现乘积后面一项是一个明显的等差数列求和:
所以最终答案即为:
时间复杂度为 。
这道题的取模和数据大小很烦人,建议答案计数器使用
CPPint128 类型(其他地方不要多用,会超时)。#include <bits/stdc++.h>
#define int long long
#define int128 __int128_t
using namespace std;
const int mod = 998244353, inv2 = 499122177;
int s(int n){
if(n == 0) return 0;
int128 cnt = 0;
for(int k = 1; 1LL << (k - 1) <= n; k ++){
int l = 1LL << (k - 1);
int r = min((1LL << k) - 1, n);
int128 t = (2 * n - l - r + 2) % mod;
t = (t * (r - l + 1)) % mod;
t = (t * inv2) % mod;
t = (t * k) % mod;
cnt += t;
if(cnt >= mod) cnt -= mod;
}
return cnt;
}
void write(int128 x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
void solve()
{
int L, R; cin >> L >> R;
write((s(R) - s(L - 1) + mod) % mod); putchar('\n');
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T; cin >> T;
while(T --) solve();
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...