偶然发现了学长发给我的一个学长的学长也是我的学长的一个数论
p
p
t
ppt
ppt,先不着急复习莫反杜教筛,按这个顺序来吧
0.随便说说
前一阵子确实学习状态不是很好,我感觉我个人学习状态也是忽好忽坏的,不过只要在学习状态好的时候多学点感觉就可以学到很多东西。
看到学长之前发给我的
p
p
t
ppt
ppt感慨也很多,也跟学长聊了聊,这些知识都是别人高中甚至初中就学会的知识点,如果高中时候好好学
O
I
OI
OI,会不会现在有不一样的未来呢。其实我对现在也很满意了,能做一个普通学校的普通的
A
C
M
e
r
ACMer
ACMer就已经很满足了,起码也在热爱的路上,希望能拿点成绩出来吧。
1.随便讲讲
类欧几里得算法可以在
O
(
log
n
)
O(\log{n})
O(logn)的时间复杂度解决如下形式的前缀和函数
f
(
a
,
b
,
c
,
n
)
=
∑
i
=
0
n
⌊
a
i
+
b
c
⌋
f(a,b,c,n)=\sum_{i=0}^n \lfloor\frac{ai+b}{c}\rfloor
f(a,b,c,n)=i=0∑n⌊cai+b⌋
这个式子的向下取整让我们很容易联想到数论分块,可惜数论分块不能解决这个问题,因此我们要用到类欧几里得算法。
前置知识:几个和向上向下取整有关的不等式
ok现在开始推导,分成两种情况,第一种是
a
≥
c
a\ge c
a≥c或
b
≥
c
b\ge c
b≥c,第二种是
a
<
c
a<c
a<c且
b
<
c
b<c
b<c
可以看到,第一种情况我们直接将
a
,
b
a,b
a,b缩小到了
c
c
c以下,第二种情况我们交换了
a
,
c
a,c
a,c的位置,数字对
(
a
,
c
)
(a,c)
(a,c)的变换与求解
g
c
d
(
a
,
c
)
gcd(a,c)
gcd(a,c)时的变换相同,因此时间复杂度为
O
(
log
n
)
O(\log n)
O(logn)
2.代码实现
看起来复杂,但由于用到的是递归的思想,所以并不难
3.例题
AtCoder ABC283Ex Popcount Sum
题面
p
o
p
c
o
u
n
t
(
x
)
=
∑
i
=
0
log
2
x
(
x
>
>
i
)
&
1
=
∑
i
=
0
log
2
x
(
(
x
>
>
i
)
−
(
(
x
>
>
i
+
1
)
<
<
1
)
)
=
∑
i
=
0
log
2
x
(
⌊
x
2
i
⌋
−
2
⌊
x
2
i
+
1
⌋
)
popcount(x)=\sum_{i=0}^{\log_2^x}(x>>i)\&1=\sum_{i=0}^{\log_2^x}((x>>i)-((x>>i+1)<<1))=\sum_{i=0}^{\log_2^x}(\lfloor\frac{x}{2^i}\rfloor-2\lfloor\frac{x}{2^{i+1}}\rfloor)
popcount(x)=i=0∑log2x(x>>i)&1=i=0∑log2x((x>>i)−((x>>i+1)<<1))=i=0∑log2x(⌊2ix⌋−2⌊2i+1x⌋)
所以本题所求
∑ j m o d m = r n p o p c o u n t ( j ) = ∑ j = 0 ⌊ n − r m ⌋ p o p c o u n t ( m j + r ) = ∑ j = 0 ⌊ n − r m ⌋ ∑ i = 0 log 2 m j + r ( ⌊ m j + r 2 i ⌋ − 2 ⌊ m j + r 2 i + 1 ⌋ ) \sum_{j \mod m=r}^{n}popcount(j)=\sum_{j=0}^{\lfloor\frac{n-r}{m}\rfloor}popcount(mj+r)=\sum_{j=0}^{\lfloor\frac{n-r}{m}\rfloor}\sum_{i=0}^{\log_2^{mj+r}}(\lfloor\frac{mj+r}{2^i}\rfloor-2\lfloor\frac{mj+r}{2^{i+1}}\rfloor) jmodm=r∑npopcount(j)=j=0∑⌊mn−r⌋popcount(mj+r)=j=0∑⌊mn−r⌋i=0∑log2mj+r(⌊2imj+r⌋−2⌊2i+1mj+r⌋)
= ∑ i = 0 log 2 m j + r ∑ j = 0 ⌊ n − r m ⌋ ( ⌊ m j + r 2 i ⌋ − 2 ⌊ m j + r 2 i + 1 ⌋ ) = ∑ i = 0 log 2 m j + r ( f ( m , r , 2 i , ⌊ n − r m ⌋ ) − 2 f ( m , r , 2 i + ! , ⌊ n − r m ⌋ ) ) =\sum_{i=0}^{\log_2^{mj+r}}\sum_{j=0}^{\lfloor\frac{n-r}{m}\rfloor}(\lfloor\frac{mj+r}{2^i}\rfloor-2\lfloor\frac{mj+r}{2^{i+1}}\rfloor)=\sum_{i=0}^{\log_2^{mj+r}}(f(m,r,2^i,\lfloor\frac{n-r}{m}\rfloor)-2f(m,r,2^{i+!},\lfloor\frac{n-r}{m}\rfloor)) =i=0∑log2mj+rj=0∑⌊mn−r⌋(⌊2imj+r⌋−2⌊2i+1mj+r⌋)=i=0∑log2mj+r(f(m,r,2i,⌊mn−r⌋)−2f(m,r,2i+!,⌊mn−r⌋))
前面一个 l o g log log ,计算 f f f 还有一个 l o g log log ,时间复杂度是 l o g log log 方的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline void read(ll &x){
ll s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
x=s*w;
}
ll f_gcd(ll a, ll b, ll c, ll n){
ll res=n*(n+1)/2*(a/c)+(n+1)*(b/c);
a%=c,b%=c;ll m=(a*n+b)/c;
if(m==0)return res;
return res+n*m-f_gcd(c,c-b-1,a,m-1);
}
ll t,n,m,r;
int main(){
read(t);
while(t--){
read(n),read(m),read(r);
ll k=log(n)/log(2),p=1,ans=0;
for(ll i=0;i<=k;i++){
ans+=f_gcd(m,r,p,(n-r)/m)-2*f_gcd(m,r,(p<<1),(n-r)/m);
p<<=1;
}
printf("%lld\n",ans);
}
}