51nod 1837 砝码称重 (规律)

Description

小 Q 有 n 个砝码,它们的质量分别为 1 克、 2 克、……、 n 克。

他给 i 克的砝码标上了编号 i (i = 1, 2, ..., n),但是编号被人打乱了,即编号为 i 的砝码不一定是 i 克,而是 a_i 克,这里 a 指的是 1 到 n 的一个排列。

他有一杆天平,可以向天平的两侧放任意数量的砝码,通过一次称量得到两侧质量的大小关系,关系只有左侧重、一样重、右侧重三种可能。

他想知道,最坏情况下,他至少需要称量多少次,才能确定其中至少一个编号为 i 的砝码的质量是 i 克或不是 i 克。

提示:这里所谓的最坏情况是指,对于固定的、按顺序进行的称量操作,不论每次称量的结果是什么,都能完成所需完成的上述判定任务。

例如 n = 6 时,可以只称量一次,选择编号为 1、 2、 3 的砝码放在左侧,编号为 6 的砝码放在右侧。

如果天平不是平的,则可以确定存在至少一个砝码 i 不是 i 克 (i = 1, 2, 3, 6),否则编号为 6 的砝码一定是 6 克。

再例如 n = 5 时,可以只称量两次,第一次选择编号为 2、3 的砝码放在左侧,编号为 5 的砝码放在右侧,第二次选择编号为 1、4 的砝码放在左侧,编号为 5 的砝码放在右侧。

这里略去这样称量的正确性,留给做题人推导和证明。

 

Input

输入包含多组测试数据。

每行对应一组测试数据,包含一个正整数 n 。

不超过 10^5 组数据,1 ≤ n ≤ 10^9。

 

Output

每行对应一组测试数据,输出一个正整数表示答案。

 

Input 示例

1
5
6

 

Output示例

0
2
1

 

思路

最坏情况就是每次称量的结果都和把 $a_i$ 当作 $i$ 称量的结果相同,所以只用考虑在这种情况下要多少次称量才能称量出至少一定有一个 $ai=i$ 。

显然答案不会超过 $3$ ,因为高斯证明过任意一个正整数可以表示成三个三角形数的和。

实际上答案最大是 $2$ ,通过观察或模拟,我们可以得到以下结论:

  • 当 $n$ 是三角形数时(即 $n=\frac{k(k+1)}{2}$ ),只需一次,称量 $1+2+⋯+k=n$ 是否成立,因为任选 $k$ 个砝码能组成的最小质量和是 $n$ 。
  • 当 $n$ 是三角形数 $+1$ 时(即 $n=\frac{k(k+1)}{2}+1$ ),只需一次,称量 $1+2+⋯+k<n$ 是否成立,原因类似于上一个结论。
  • 当第 $n$ 个三角形数是平方数时(即 $\frac{n(n+1)}{2}=k^2$ ),只需一次,称量 $1+2+⋯+(k−1)=(k+1)+(k+2)+⋯+n$ 是否成立,因为去掉一个砝码后能够拆分成两个质量和相同的砝码区间只有一种方案。
  • 当第 $n$ 个三角形数是平方数 $+1$ 时(即 $\frac{n(n+1)}{2}=k^2+1$ ),只需一次,称量 $1+2+⋯+(k−1)<(k+1)+(k+2)+⋯+n$ 是否成立,原因类似上一个结论。
  • 如果 $n,n−1,n−2$ 之中至少有一个数可以表示成两个三角形数的和,则只需要称两次,因为小于号可以使用至多两次
  • 对于上面不能覆盖到的其他情况,可以找到三个非负整数 $a≤b≤c<n$ 满足 $\frac{a(a+1)}{2}+\frac{b(b+1)}{2}+\frac{c(c+1)}{2}=n+\frac{n(n+1)}{2}$ ,对于 $c≤n−4,c=n−3,c=n−2,c=n−1$ 的情况可以利用 $\frac{c(c+1)}{2}−n$ 分别构造出称两次的结果。

这一个序列叫做 Baron Munchhausen's Sequence

相关证明:https://arxiv.org/pdf/1003.3406v1.pdf

判断一个数是不是平方数只需要将其开根下取整再平方进行检验,判断三角形数同理。

 

AC 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main()
{
    LL n;
    while(cin>>n)
    {
        if(n==1)
        {
            cout<<0<<endl;
            continue;
        }
        LL low = ((LL)sqrt(8*n+1)-1)/2;
        LL sjx = low*(low+1)/2;
        LL ls = n*(n+1)/2;
        LL sq = (LL)sqrt(ls)*(LL)sqrt(ls);
        if(sjx == n || sjx + 1 == n)
            cout<<1<<endl;
        else if(sq == ls||sq + 1 ==ls)
            cout<<1<<endl;
        else cout<<2<<endl;
    }
    return 0;
}