事情是这样的。。。
最近校内集训有这样一道题:
给定一个N(N<1e12),问有多少个M使得 (N%M)|M.
校内绝大部分的人(其实就是除了我外)都是用质因数分解过的,大概是:
因为gcd(n,m)=gcd(n%m,m),且gcd(n%m,m)==n%m (题意),所以就有gcd(n,m)=n%m.
枚举a=n%m,则要n=ax,m=ay 满足 gcd(x,y)=1.
又因为m|n-a (n-n%m),两边除以a,有y|x-1,此时显然有gcd(x,y)=1,所以上面的限制可以直接忽略。
那么再统计x-1的因数个数即可。
复杂度大约为N的所有约数的约数个数,有点绕...
这种做法太暴力且复杂度玄学,但这做法竟然跑得飞快。。。
于是我抱着一股愤怒的心情写下这篇博客,来推广一下新数论分块。
------------------------------------------------------我是萌萌哒的分割线--------------------------------------------------------
这其实是一道新数论分块(暂且叫它这个名字)的板子题。
先说结论:
类似于数论分块中, ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋的有有效取值只有 O ( n ) O(\sqrt n) O(n)个, ( ⌊ n i ⌋ , ⌈ i n % i ⌉ ) (\lfloor \frac{n}{i}\rfloor,\lceil\frac{i}{n\%i}\rceil) (⌊in⌋,⌈n%ii⌉)的有效取值只有 O ( n ln n ) O(\sqrt n \ln n) O(nlnn)个。
( i ∣ n i|n i∣n时单独划一组,显然这最多不超过 O ( n ) O(\sqrt n) O(n)组)
(上述的取整都可以分别改成上取整和下取整,这见仁见智)
推导过程如下:
令 a = ⌊ n i ⌋ , b = ⌈ i n % i ⌉ 令a=\lfloor \frac{n}{i}\rfloor,b=\lceil\frac{i}{n\%i}\rceil 令a=⌊in⌋,b=⌈n%ii⌉,并假定存在一个常数S.
那么对于同一个a而言:
1.若 b ≤ S b\leq S b≤S时,则 ( a , b ) (a,b) (a,b)最多有S组。
2.若 b > S b> S b>S时:
首先有 ⌊ n i ⌋ = a ⇔ n i ≥ a ⇔ i ≤ n a \lfloor \frac{n}{i}\rfloor=a \Leftrightarrow \frac{n}{i}\geq a \Leftrightarrow i \leq \frac{n}{a} ⌊in⌋=a⇔in≥a⇔i≤an;
其次有 ⌈ i n % i ⌉ = ⌈ i n − a i ⌉ > S ⇒ ⌊ i n − a i ⌋ ≥ S ⇔ i n − a i ≥ S ⇔ i ≥ S ⋅ ( n − a i ) ⇔ i ≥ S n a S + 1 = n a + 1 S \lceil\frac{i}{n\%i}\rceil=\lceil\frac{i}{n-ai}\rceil > S \Rightarrow \lfloor\frac{i}{n-ai}\rfloor \geq S \Leftrightarrow \frac{i}{n-ai} \geq S \Leftrightarrow i \geq S\cdot(n-ai) \Leftrightarrow i \geq \frac{Sn}{aS+1}=\frac{n}{a+\frac{1}{S}} ⌈n%ii⌉=⌈n−aii⌉>S⇒⌊n−aii⌋≥S⇔n−aii≥S⇔i≥S⋅(n−ai)⇔i≥aS+1Sn=a+S1n
即 n a ≤ i ≤ n a + 1 S \frac{n}{a}\leq i\leq \frac{n}{a+\frac{1}{S}} an≤i≤a+S1n,显然 i i i的整数取值最多只有 n a + 1 S − n a = n a + a 2 S ≤ n a 2 S \frac{n}{a+\frac{1}{S}}-\frac{n}{a}=\frac{n}{a+a^2S}\leq\frac{n}{a^2S} a+S1n−an=a+a2Sn≤a2Sn
综合1和2,取 S = n a S=\frac{\sqrt{n}}{a} S=an,则有 S + n a 2 S = O ( n a ) S+\frac{n}{a^2S}=O(\frac{\sqrt{n}}{a}) S+a2Sn=O(an)
则不同的a值的总贡献大约为 ∑ a = 1 n O ( n a ) = O ( ∑ a = 1 n n a ) = O ( n ⋅ ∑ a = 1 n 1 a ) = O ( n ln n ) \sum_{a=1}^{n}O(\frac{\sqrt{n}}{a})=O(\sum_{a=1}^{n}\frac{\sqrt{n}}{a})=O(\sqrt{n}\cdot\sum_{a=1}^{n}\frac{1}{a})=O(\sqrt n\ln n) ∑a=1nO(an)=O(∑a=1nan)=O(n⋅∑a=1na1)=O(nlnn)
而实际上程序跑出来的界也十分接近 n ln n \sqrt n\ln n nlnn。
回到这题,其实就是问有多少个 M M M使得 M N % M = ⌈ M N % M ⌉ \frac{M}{N\%M}=\lceil \frac{M}{N\%M} \rceil N%MM=⌈N%MM⌉,直接套用上面的分块即可。
同时也可以证明答案不会超过 N ln N \sqrt N\ln N NlnN,然而这给直接质因数分解提供了很好的复杂度证明.
-------------------------------------------------------我还是萌萌哒的分割线--------------------------------------------------------
具体写法:
先按数论整除分块,确定出 [ i , j ] , ( ∀ x ∈ [ i , j ] , ⌊ n x ⌋ = a ( 常 数 ) ) [i,j],(\forall x \in[i,j],\lfloor \frac{n}{x}\rfloor=a(常数)) [i,j],(∀x∈[i,j],⌊xn⌋=a(常数))
观察到 ∀ x ∈ [ i , j ] , ⌈ x n % x ⌉ = ⌈ x n − a x ⌉ \forall x \in[i,j],\lceil\frac{x}{n\%x}\rceil=\lceil\frac{x}{n-ax}\rceil ∀x∈[i,j],⌈n%xx⌉=⌈n−axx⌉是一个不减的函数。
所以对于一个 l l l(令 ⌈ l n % l ⌉ = ⌈ l n − a l ⌉ = b \lceil\frac{l}{n\%l}\rceil=\lceil\frac{l}{n-al}\rceil=b ⌈n%ll⌉=⌈n−all⌉=b),要确定一个最大 r r r使得 [ l , r ] , ∀ x ∈ [ l , r ] , ⌈ x n − a x ⌉ = b ( 常 数 ) [l,r],\forall x \in[l,r],\lceil\frac{x}{n-ax}\rceil=b(常数) [l,r],∀x∈[l,r],⌈n−axx⌉=b(常数),
即要找到最大的一个 r r r使得 ⌈ r n − a r ⌉ ≤ b \lceil\frac{r}{n-ar}\rceil\leq b ⌈n−arr⌉≤b。
那么剩下就是简单地推一下公式了:
⌈ r n − a r ⌉ ≤ b ⇔ r n − a r ≤ b ⇔ r ≤ b ⋅ ( n − a r ) ⇔ r ≤ b n a b + 1 \lceil\frac{r}{n-ar}\rceil\leq b \Leftrightarrow \frac{r}{n-ar} \leq b \Leftrightarrow r\leq b\cdot(n-ar) \Leftrightarrow r\leq \frac{bn}{ab+1} ⌈n−arr⌉≤b⇔n−arr≤b⇔r≤b⋅(n−ar)⇔r≤ab+1bn
即 r = ⌊ b n a b + 1 ⌋ r=\lfloor \frac{bn}{ab+1} \rfloor r=⌊ab+1bn⌋
注意 x ∣ n x|n x∣n的情况要特判.
for (LL i=1,a,j;i<=n;i=j+1) {
j=n/(a=n/i);
for (LL l=1,b,r;l<=j;l=r+1) {
if (n%l==0) {
assert(l==j);//当x|n时一定出现在整除分块的末尾处。
//....
} else {
b=l/(n%l);
r=b*n/(a*b+1);
//.....
}
}
}