CF1796C Maximum Set
当天比赛打完之后看了看跟我排名差不多的人的这题代码,感觉莫名其妙,写了几十行。我看了看我只有十几行的 AC 代码,陷入了沉思。
分析
题目的要求其实可以转换为:在区间
接下来题目让我们求这样的集合个数。显而易见的一个暴力就是从左端点开始枚举,一直枚举到不存在符合要求的集合。这样可能会炸,所以考虑优化。
考虑二分答案。
怎么可能二分答案(虽然也不是不行)!显然我们可以把刚才那个式子拿过来用:
你真以为就这么简单?
不可能的。
显然,在使选出的数最多的情况下,后一个数并不一定非要是前一个数的两倍。也可以是,比如说三倍,四倍什么的。在抱怨这题之前,让我们先仔细看看:后面一个数可以是前一个数的四倍吗?
显然不行。
若后面一个数是前一个数的四倍,那完全可以在中间再插一个数进去,使选出的数变多。顺着这个思路往下想,后面的数是前面数的五倍,六倍,甚至七倍,八倍,那显然也不行。所以在使得选出的数最多的情况下,后面的数最多是前面数的三倍。
猜你在想:排列组合。
你先别急。让我们接着想:若在选出的集合中有三个连续的数,分别叫
发现什么问题?刚才的讨论说明了后面的数最多是前面数的三倍,而且最多只能有一个数是其前面数的三倍。于是我们可以再算出
一个临界点
什么?你不会还想二分答案吧!别啊,我们有更加迅速的做法:先前那个式子
你真以为就这么简单?
是的,真就这么简单。写的时候注意一下精度问题就好了。
这里最终的
代码
#include <iostream>
#include <math.h>
#define int long long
using namespace std;
const int p = 998244353;
signed main() {
int tc;
cin >> tc;
while (tc--) {
int a, b;
cin >> a >> b;
int x = log2((1.0 * b) / a);
int l = a, m = (b / 3) / (1 << x - 1), r = b / (1 << x); // l 是 l0, r 是 l1,m 是 l2,x 是 k。
cout << x + 1 << " " << (r - l + 1 + (((m - l + 1) % p) * (x % p) * (m >= l)) % p) % p << "\n";
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】