事情是这样的。。。
最近校内集训有这样一道题:
给定一个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);
//.....
}
}
}