专栏文章
P13831 【MX-X18-T3】「FAOI-R6」比亚多西-题解
P13831题解参与者 1已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mip7d2v4
- 此快照首次捕获于
- 2025/12/03 07:21 3 个月前
- 此快照最后确认于
- 2025/12/03 07:21 3 个月前
P13831 【MX-X18-T3】「FAOI-R6」比亚多西-题解
upd:优化证明,补充知识点。
看了官方解答后才发现自己的做法有多么【数据删除】,给大家分享一下。
简要题意
有一个区间 和一个数 。我们在区间上做二分查找:
- 每次取中点 ,操作次数 加一;
- 如果 ,过程结束;
- 否则按二分方式更新区间,继续。
最终 就是找到 所需的二分次数。记这个值为 。
对 定义:
对 定义:
即对所有 的查找次数求和。
现在给你多个询问 ,需要计算:
题目分析
我们先考虑函数 的求法。手玩容易发现 是由一个 、两个 、四个 等类似的形式组成,这实际上对应了完全二叉树中每层的节点的深度。
我们可以用类似于线段树的方法建树,举个例子:
如果我们对区间 做二分,那么根节点是 ,它的左右子树分别对应 和 。
举个例子,当 时的树:

-
实际上,这样的树被叫做二叉搜索树,对于二叉搜索树,它或者是一棵空树,或者是具有下列性质的二叉树:
-
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
-
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
-
它的左、右子树也分别为二叉排序树。
-
显然,这样构造出的树深度是 (根位于第 层)。而每个元素 的深度 就是它在树中的层数,所以 就等价于“树上所有结点的深度和”。
我们可以利用下面公式求出 :
证明
对于第 层(),该层结点数为 ,它们的深度都是 ,因此对总深度的贡献为:
最后一层 的结点数为:
它们的深度都是 ,因此贡献为:
把各层贡献相加,就得到总深度和:
考虑化简下面这个式子:
我们可以考虑利用恒等式 ,但是这太考验注意力了,怎么办?
你可以发现 是容易计算的:
考虑 与 的关系:
因此:
代回到 ,整理可得 :
其实这个东西打完暴力 oeis 可以搜到。
很显然的是 在形如 的区间上不变,从而考虑二进制分组批量求和。
在区间 内,每个 的贡献是:
那么区间和就是:
其中 用可以等差数列求和:
代码实现
二进制分组感觉实现细节比较多,需要开
__int128,洛谷可过,梦熊需要卡常。可能需要优雅地取模。FUN FACT:赛时我在 MXOJ 上提交了近 发,最后一个点卡在了
CPP1015ms。#include <bits/stdc++.h>
using namespace std;
#define int __int128
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
inline void print(int n)
{
if(n<0)
{
putchar('-');
n*=-1;
}
if(n>9) print(n/10);
putchar(n % 10 + '0');
}
const int mod=998244353;
const int inv=499122177;
// 2 在 998244353 下的逆元
int f(int n)
{
int ans=0;
for(int i=1; i<=61; i++) // 枚举 h 的取值范围(因为 n ≤ 2^61),区间 [l, r] 上 h 恒等于 i
{
int l=(1ll<<(i-1)); // 区间左端点 l = 2^{i-1}
if(l>n) break; // 如果左端点已经超过 n,后面区间都无效
int r=(1ll<<i)-1; // 区间右端点 r = 2^i - 1
if(n<r) r=n; // 被 n 截断,只取到 n
int len=(r-l+1)%mod;
l%=mod;
r%=mod;
int sum=(l+r)*len*inv%mod;
ans=(ans+i*sum-len*(1ll<<i)%mod+len*(i+1)+mod)%mod;
}
return ans;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T=read();
while(T--)
{
int L=read(),R=read();
print((f(R)-f(L-1)+mod)%mod);
putchar('\n');
}
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...