嘟嘟噜 解题报告

嘟嘟噜

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

posted @ 2018-10-19 14:40  露迭月  阅读(214)  评论(0编辑  收藏  举报