[CP / Codeforces] E. Vlad and an Odd Ordering(Div. 4)

E. Vlad and an Odd Ordering

image


我很喜欢这道题,第一是因为它对我来说很难,第二是因为它体现了数学中的一些美妙而神奇的性质。惭愧的是,初见时我并没能做出来,看了 editorial 后也似懂非懂,直到两个星期之后,才总算理清楚思路,并且给出了一种略微复杂的解法——不知道和官方题解是否重合。

题目大意是,你有标着 1nn 张卡片,现在你需要按如下顺序将它们依次摆放:

  • 首先从小到大摆放所有标着奇数的卡片;
  • 然后从小到大摆放所有标着奇数 ×2 的卡片;
  • 然后从小到大摆放所有标着奇数 ×3 的卡片;
  • ……
  • 直到所有卡片都被摆放完毕。

问第 k 张被摆放的卡片上面标的数字是什么(为方便,以下省略 “卡片” 一词)。

观察数据规模,如果暴力模拟的话必然会超时,所以需要研究其背后的规律。

定义第 i 轮摆放的数字等于 “奇数 ×i”,为了找到突破口,我先模拟了一下 n=12 时的情况:

  • 第 1 轮:[1 3 5 7 9 11]
  • 第 2 轮:[2 6 10]
  • 第 3 轮:[]
  • 第 4 轮:[4 12]
  • 第 5 轮:[]
  • 第 6 轮:[]
  • 第 7 轮:[]
  • 第 8 轮:[8]

注意到只有 2i1 轮时才有数字被摆放,这是巧合吗?下面展开分析。

首先,第一轮摆放的必定是所有的奇数,这意味着除 1 以外的奇数轮(比如第 357 轮)都不会摆放任何数字,因为奇数和奇数的乘积依然是奇数,而所有的奇数已在第一轮被摆放完了。

观察第 6 轮,它是偶数轮,但它同样没有摆放任何数字,因为 “奇数 ×6” 中,6=2×3,所以在这一轮将要摆放的数字在第 2 轮中已经出现过了。

更一般地,我们会发现,所有满足 i=2wmm 为大于 1 的奇数,w 为正整数)的轮次都不会摆放任何数字,因为该轮次本应该摆放的数字早就在第 2w 轮中出现过了。

因此得出结论,只有在轮次为 2w 时才会摆放数字,所以我们可以统计每个这样的轮次摆放数字的总个数,逐个累加,直到找到 k 所处的轮次,且 k该轮次内 是第 j 个数,那么第 k 个被摆放的数字就等于 2w×(2j1)

每个轮次摆放的数字总个数可以通过以下方法获得:

  • 计算 a=n2w 得到每一轮所乘的最大奇数
  • 如果 a 是偶数,则将其减一
  • 再通过 b=a2 得到该轮所乘奇数的总个数,也即该轮摆放的数字个数

因此得到代码:

代码 1(O(logn)

void solve() {
int n, k;
std::cin >> n >> k;
int cnt = 0, w = 1, t;
while (1) {
t = n / w;
t -= !(t & 1);
t = (t + 1) / 2;
if (cnt + t >= k)
break;
cnt += t;
w <<= 1;
}
std::cout << ((k - cnt) * 2 - 1) * w << '\n';
}

官方题解提供了两种做法:第一种和我的解法类似,第二种采用了更为简洁的递归,但原理和前面其实是相同的,代码附在下面。

代码 2(O(logn)

int dfs(int n, int k) {
if (k <= (n + 1) / 2) {
return k * 2 - 1;
}
return 2 * dfs(n / 2, k - (n + 1) / 2);
}
void solve() {
int n, k, ans = 0;
std::cin >> n >> k;
std::cout << dfs(n, k) << '\n';
}
posted @   ZXPrism  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示