递推预处理floor(log_2{n})
在C++中,除了使用<cmath>
中的log
或log2
函数求对数,也可以通过递推求出所有可能用到的⌊log2i⌋,i∈[1,n]\lfloor \log_2i\rfloor, i\in[1, n]⌊log2i⌋,i∈[1,n]
证明:⌊log2i⌋=⌊log2⌊i2⌋⌋+1\lfloor \log_2i \rfloor=\lfloor \log_2 \lfloor \frac{i}{2} \rfloor \rfloor+1⌊log2i⌋=⌊log2⌊2i⌋⌋+1
由于log2i=log2(i2⋅2)=log2i2+log22=log2i2+1\log_2i=\log_2(\frac{i}{2}\cdot 2)=\log_2\frac{i}{2}+\log_22=\log_2\frac{i}{2}+1log2i=log2(2i⋅2)=log22i+log22=log22i+1
等号两边都向下取整,得:
⌊log2i⌋=⌊log2i2⌋+1\lfloor \log_2i \rfloor =\lfloor \log_2\frac{i}{2} \rfloor+1⌊log2i⌋=⌊log22i⌋+1
- 如果iii是偶数,则i2=⌊i2⌋\frac{i}{2}=\lfloor \frac{i}{2} \rfloor2i=⌊2i⌋,因此有
⌊log2i⌋=⌊log2i2⌋+1=⌊log2⌊i2⌋⌋+1\lfloor \log_2i \rfloor =\lfloor \log_2\frac{i}{2} \rfloor+1 = \lfloor \log_2\lfloor \frac{i}{2} \rfloor \rfloor+1⌊log2i⌋=⌊log22i⌋+1=⌊log2⌊2i⌋⌋+1 - 如果iii是奇数,则i−12=⌊i2⌋\frac{i-1}{2}=\lfloor \frac{i}{2} \rfloor2i−1=⌊2i⌋
设⌊log2i−12⌋=x\lfloor \log_2\frac{i-1}{2} \rfloor =x⌊log22i−1⌋=x
那么x≤log2i−12<x+1x\le \log_2\frac{i-1}{2}<x+1x≤log22i−1<x+1
2x≤i−12<2x+12^x\le \frac{i-1}{2}<2^{x+1}2x≤2i−1<2x+1
由于i−12\frac{i-1}{2}2i−1是整数,2x+12^{x+1}2x+1也是整数,较小的整数加上12\frac{1}{2}21后,一定小于较大的整数。
因此有2x≤i−12<i−12+12=i2<2x+12^x\le \frac{i-1}{2}<\frac{i-1}{2}+\frac{1}{2}=\frac{i}{2}<2^{x+1}2x≤2i−1<2i−1+21=2i<2x+1
所以x≤log2i2<x+1x\le \log_2{\frac{i}{2}}<x+1x≤log22i<x+1
因此⌊log2i2⌋=x=⌊log2i−12⌋=⌊log2⌊i2⌋⌋\lfloor \log_2{\frac{i}{2}} \rfloor =x= \lfloor \log_2\frac{i-1}{2} \rfloor= \lfloor \log_2\lfloor \frac{i}{2}\rfloor \rfloor⌊log22i⌋=x=⌊log22i−1⌋=⌊log2⌊2i⌋⌋
因此⌊log2i⌋=⌊log2i2⌋+1=⌊log2⌊i2⌋⌋+1\lfloor \log_2i \rfloor =\lfloor \log_2\frac{i}{2} \rfloor+1 = \lfloor \log_2\lfloor \frac{i}{2} \rfloor \rfloor+1⌊log2i⌋=⌊log22i⌋+1=⌊log2⌊2i⌋⌋+1
证毕。
已知⌊log21⌋=0\lfloor \log_21 \rfloor=0⌊log21⌋=0,结合递推式⌊log2i⌋=⌊log2⌊i2⌋⌋+1\lfloor \log_2i \rfloor=\lfloor \log_2 \lfloor \frac{i}{2} \rfloor \rfloor+1⌊log2i⌋=⌊log2⌊2i⌋⌋+1即可递推得到所有可能用到的⌊log2i⌋,i∈[1,n]\lfloor \log_2i\rfloor, i\in[1, n]⌊log2i⌋,i∈[1,n]。
代码如下:
const int N = 1000005;//N设为n可能达到的最大值
int lg[N];//lg[i]:floor(log_2{i})
void initLg(int n)//生成lg[1]~lg[n]
{//全局变量初值为0,已经设好lg[1] = 0for(int i = 2; i <= n; ++i)lg[i] = lg[i/2]+1;
}
预处理lg数组的时间复杂度为O(n)O(n)O(n)