YACS 2022年8月月赛 甲组 T1 约瑟夫问题 题解
又来填坑了(大雾
#1.为什么用树状数组
做多了题目,看一眼这题就知道要用数据结构了,进一步分析就可以知道这是一道二分和树状数组的题目。(其实用变形的链表
# 2.具体思路
首先设定
这样,设当前在第
# 3.how to do it?
确定完正确思路后,来看怎么求。
这是这道题的关键,我们需要使用 二分 算法,(我这里用的是倍增,更加好写)。
首先看要出局的人在什么位置,因为有可能跨越一个环。
如果
当锁定了答案范围之后,现在来讨论如何二分。取
我们可以转换成二分最后一个位置
另外,每次二分完别忘了在这个位置加上
然后就能愉快地 AC 神仙分块甲组题了。
# 4.闲话(其他做法)
其实我也口胡了一个线段树上二分的做法,但是胡完感觉有点困难,
大概是这样的:这里的线段树二分不是普通的线段树二分,还需要一个备用量
关于变形的链表,每个位置不仅存储下一个元素的位置,还会存储下
代码很短,压了点行:
#include <iostream> using namespace std; int n, now = 1; int a[500005], c[500005]; void add (int x, int y) {for (; x <= n; x += x & -x) c[x] += y;} int query (int x) {return x == 0 ? 0 : c[x] + query (x - (x & -x) );} int sum (int x, int y) {return query (y) - query (x - 1);} int q (int x) {//求 now(包含)后面第 x 个不为 0 的。 int l = 1, r = n, res = sum (now, n); if (res >= x) { l = now; r = n; } else { x -= res; l = 1, r = now - 1; } int ret = l - 1; for (int i = 19; i >= 0; i --) if (ret + (1 << i) <= n && sum (l, ret + (1 << i) ) < x) ret += 1 << i; return ret + 1; } int main () { scanf ("%d", &n); for (int i = 1; i <= n; i ++) { scanf ("%d", &a[i]); ++ a[i]; a[i] %= (n - i + 1); add (i, 1); } for (int i = 1; i <= n; i ++) { if (a[i] == 0) a[i] += n - i + 1; int x = q (a[i]); printf ("%d\n", x); add (x, -1); now = q (a[i]); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异