嘟嘟噜 解题报告
嘟嘟噜
Description
由于众所周知的原因, 冈部一直欠真由理一串香蕉.
为了封上真由理的嘴, 冈部承诺只要真由理回答出这个问题, 就给她买一车的香蕉:
一开始有 \(n\) 个人围成一个圈, 从 \(1\) 开始顺时针报数, 报出 \(m\) 的人被机关处决. 然后下一
个人再从 \(1\) 开始报数, 直到只剩下一个人.
红莉栖: “这不就是约瑟夫问题吗...”
伦太郎: “助手你给我闭嘴!”
真由理虽然已经晕头转向了, 但听到有一车的香蕉, 两眼便放出了光芒.
“那个呢, 真由氏很想要一车子的香蕉呢. 如果可以帮帮我的话, 我可以把一些香蕉分给你哟, 诶
嘿嘿. 拜托你啦.”
Input Format
第一行一个整数 \(T\) , 表示数据组数.
接下来 \(T\) 行, 每行两个整数 \(n\) ,\(m\).
Output Format
对于每组数据, 输出一行一个整数, 表示幸存者的编号
Constraints
测试点编号 | n | m |
---|---|---|
\(1\) | \(\le 10^5\) | \(\le 10^5\) |
\(2\) | \(\le 10^6\) | \(\le 10^5\) |
\(3\) | \(\le 10^9\) | \(\le 2\) |
\(4\) | \(\le 10^9\) | \(\le 2\) |
\(5\) | \(\le 10^9\) | \(\le 3\) |
\(6\) | \(\le 10^9\) | \(\le 3\) |
\(7\) | \(\le 10^9\) | \(\le 10^3\) |
\(8\) | \(\le 10^9\) | \(\le 10^4\) |
\(9\) | \(\le 10^9\) | \(\le 10^5\) |
\(10\) | \(\le 10^9\) | \(\le 10^5\) |
对于\(100\%\)的数据,\(1 \le T \le 20\),\(1 \le n \le 10^9\),\(1 \le m \le 10^5\)
Solution
其实就是约瑟夫问题,有个数学结论,考试的时候wjyyy神仙打表打出来了,我\(20pts\)的平衡树暴力都打挂了。
结论:令\(f_i\)代表有\(i\)个人从头开始删最后一个人的编号,则有
\[f_i=\left\{\begin{aligned} 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \,i=1 \\ (f_{i-1}+m)\%i \ \ \ \ \ \ \ \ \ \ \,i<m \\ \frac{m \times (f_{i'}-i\%m)\% i'}{m-1} \ i\ge m\end{aligned}\right.
\]
\[i'=i-\lfloor\frac{i}{m} \rfloor
\]
解释一下,首先编号是从\(0\)开始的,我们每次删完一个数后,为了保证下次还是从头开始删,我们把删完后面的那个位置重新编号为\(0\),然后求解子问题。函数退回来就只要映射好编号就可以了,按照自己的理解就可以了。
Code:
#include <cstdio>
int n,m,T;
int dfs(int i)
{
int nt=i-i/m;
if(i>=m) return 1ll*((dfs(nt)-i%m)%nt+nt)%nt*m/(m-1);
return i==1?0:(dfs(i-1)+m)%i;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
printf("%d\n",dfs(n)+1);
}
return 0;
}
2018.10.19