偶然发现了学长发给我的一个学长的学长也是我的学长的一个数论
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)
看起来复杂,但由于用到的是递归的思想,所以并不难
牛客多校(六)C idol!!!
题面
题意就这一句话,意思是让你找
∏
i
=
1
n
i
!
!
\prod_{i=1}^{n}i!!
∏i=1ni!!的尾数有多少个
0
0
0
上午刚打完模板,下午就出了个能用的题,不过这题用这个方法应该是有点降维打击,有很多比这个方法简单的方法。
首先有个性质就是
n
!
!
×
(
n
+
1
)
!
!
=
(
n
+
1
)
!
n!!\times (n+1)!!=(n+1)!
n!!×(n+1)!!=(n+1)!,所以题目可变为为
∏
i
=
0
⌊
n
2
⌋
(
2
i
+
1
)
!
,
2
∤
n
,
∏
i
=
0
f
r
a
c
n
2
(
2
i
)
!
,
2
∣
n
\prod_{i=0}^{\lfloor\frac{n}{2}\rfloor}(2i+1)!,2\nmid n,\prod_{i=0}^{frac{n}{2}}(2i)!,2\mid n
i=0∏⌊2n⌋(2i+1)!,2∤n,i=0∏fracn2(2i)!,2∣n
考虑题意,要求尾数有多少个
0
0
0,那么就是要找有多少个
10
,
10,
10,也就是要找有多少对
2
,
5
2,5
2,5;进一步地,有一个
5
5
5一定至少有一个
2
2
2,所以我们找
5
5
5的数量即可。
对于
n
!
n!
n!,其
5
5
5的个数为
∑
i
=
1
⌊
log
5
n
⌋
⌊
n
5
i
⌋
\sum_{i=1}^{\lfloor\log_5n\rfloor}\lfloor\frac{n}{5^i}\rfloor
i=1∑⌊log5n⌋⌊5in⌋
那对于
∏
i
=
0
⌊
n
2
⌋
(
2
i
+
1
)
!
\prod_{i=0}^{\lfloor\frac{n}{2}\rfloor}(2i+1)!
∏i=0⌊2n⌋(2i+1)!;
∏
i
=
0
n
2
(
2
i
)
!
\prod_{i=0}^{\frac{n}{2}}(2i)!
∏i=02n(2i)!,
5
5
5的个数分别为
∑ i = 0 ⌊ n 2 ⌋ ∑ i = 1 ⌊ log 5 n ⌋ ⌊ 2 i + 1 5 i ⌋ , ∑ i = 0 ⌊ n 2 ⌋ ∑ i = 1 ⌊ log 5 n ⌋ ⌊ 2 i 5 i ⌋ \sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\sum_{i=1}^{\lfloor\log_5n\rfloor}\lfloor\frac{2i+1}{5^i}\rfloor,\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\sum_{i=1}^{\lfloor\log_5n\rfloor}\lfloor\frac{2i}{5^i}\rfloor i=0∑⌊2n⌋i=1∑⌊log5n⌋⌊5i2i+1⌋,i=0∑⌊2n⌋i=1∑⌊log5n⌋⌊5i2i⌋
交换
∑
\sum
∑的位置,有
∑
i
=
1
⌊
log
5
n
⌋
∑
i
=
0
⌊
n
2
⌋
⌊
2
i
+
1
5
i
⌋
,
∑
i
=
1
⌊
log
5
n
⌋
∑
i
=
0
⌊
n
2
⌋
⌊
2
i
5
i
⌋
\sum_{i=1}^{\lfloor\log_5n\rfloor}\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\lfloor\frac{2i+1}{5^i}\rfloor,\sum_{i=1}^{\lfloor\log_5n\rfloor}\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\lfloor\frac{2i}{5^i}\rfloor
i=1∑⌊log5n⌋i=0∑⌊2n⌋⌊5i2i+1⌋,i=1∑⌊log5n⌋i=0∑⌊2n⌋⌊5i2i⌋
即为
∑
i
=
1
⌊
log
5
n
⌋
f
(
2
,
1
,
5
i
,
⌊
n
2
⌋
)
,
n
∤
2
,
\sum_{i=1}^{\lfloor\log_5n\rfloor}f(2,1,5^i,\lfloor\frac{n}{2}\rfloor),n\nmid 2,
i=1∑⌊log5n⌋f(2,1,5i,⌊2n⌋),n∤2,
∑
i
=
1
⌊
log
5
n
⌋
f
(
2
,
0
,
5
i
,
n
2
)
,
n
∣
2
。
\sum_{i=1}^{\lfloor\log_5n\rfloor}f(2,0,5^i,\frac{n}{2}),n\mid 2。
i=1∑⌊log5n⌋f(2,0,5i,2n),n∣2。
这题会炸 l o n g l o n g long\,\,long longlong,牛客支持 i n t 128 int128 int128,找了50分钟的 i n t 128 int128 int128板子之后才知道,浪费了好久的时间
#include<bits/stdc++.h>
using namespace std;
typedef __int128 ll;
typedef long long LL;
inline void read(ll &n){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
n=x*f;
}
inline void print(ll n){
if(n<0){putchar('-');n*=-1;}
if(n>9) print(n/10);
putchar(n % 10 + '0');
}
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 n,ans;
int main(){
read(n);
for(ll i=1;i<=n/5;i=i*5){
ll now=i*5;
if(n&1)ans+=f_gcd(2ll,1ll,now,n/2);
else ans+=f_gcd(2ll,0ll,now,n/2);
}
print(ans);puts("");
return 0;
}
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);
}
}
2.扩展
现在将
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=0n⌊cai+b⌋扩展成其他形式的函数
g
(
a
,
b
,
c
,
n
)
=
∑
i
=
0
n
i
⌊
a
i
+
b
c
⌋
,
h
(
a
,
b
,
c
,
n
)
=
∑
i
=
0
n
⌊
a
i
+
b
c
⌋
2
g(a,b,c,n)=\sum_{i=0}^n i \lfloor\frac{ai+b}{c}\rfloor,h(a,b,c,n)=\sum_{i=0}^n \lfloor\frac{ai+b}{c}\rfloor^2
g(a,b,c,n)=i=0∑ni⌊cai+b⌋,h(a,b,c,n)=i=0∑n⌊cai+b⌋2
对于如上的两个函数,同
f
f
f的推导过程,我们分两种情况对
g
,
h
g,h
g,h进行推导:
g ( a , b , c , n ) : g(a,b,c,n): g(a,b,c,n):
h
(
a
,
b
,
c
,
n
)
:
h(a,b,c,n):
h(a,b,c,n):
可以发现 g ( a , b , c , n ) g(a,b,c,n) g(a,b,c,n)与 h ( a , b , c , n ) h(a,b,c,n) h(a,b,c,n)需要互相调用到对方的函数,因此递归的时候我们直接将 f , g , h f,g,h f,g,h三个函数一起递归即可
luoguP5170 【模板】类欧几里得算法
题面
打了一上午,发现自己前面推的每个函数都有问题,难绷,现在已经改正,就是一个朴素的递归。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
struct euc{
ll f,g,h;
euc(){f=g=h=0;}
euc(ll x, ll y, ll z){f=x,g=y,h=z;}
};
inline ll qpow(ll x, ll y){
ll res=1;
while(y){
if(y&1)(res*=x)%=mod;
(x*=x)%=mod,y>>=1;
}
return res;
}
const ll inv2=qpow(2LL,mod-2);
const ll inv6=qpow(6LL,mod-2);
euc ans(ll a, ll b, ll c, ll n){
euc res,nxt;
if(a>=c||b>=c||a==0){
res.f=(n*(n+1)%mod*inv2%mod*(a/c)%mod+(n+1)*(b/c)%mod)%mod;
res.g=(n*(n+1)%mod*((n*2+1)%mod)%mod*inv6%mod*(a/c)%mod+n*(n+1)%mod*inv2%mod*(b/c)%mod)%mod;
res.h=(n*(n+1)%mod*(a/c)%mod*(b/c)%mod+(n+1)*(b/c)%mod*(b/c)%mod)%mod;
(res.h+=n*(n+1)%mod*(n*2+1)%mod*inv6%mod*(a/c)%mod*(a/c)%mod)%=mod;
if(a==0)return res;
nxt=ans(a%c,b%c,c,n);
(res.f+=nxt.f)%=mod,(res.g+=nxt.g)%=mod;
(res.h+=(((a/c)*2%mod*nxt.g%mod+(b/c)*2%mod*nxt.f%mod)%mod+nxt.h)%mod)%=mod;
return res;
}
ll m=(a*n+b)/c;
nxt=ans(c,c-b-1,a,m-1);
res.f=(n*m%mod-nxt.f+mod)%mod;
res.g=((n*m%mod*(n+1)%mod-nxt.f+mod)%mod-nxt.h+mod)%mod*inv2%mod;
res.h=(((n*m%mod*(m+1)%mod-nxt.g*2%mod+mod)%mod-nxt.f*2%mod+mod)%mod-res.f+mod)%mod;
return res;
}
inline ll read(){
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();}
return s*w;
}
int main(){
ll T=read();
while(T--){
ll n=read(),a=read(),b=read(),c=read();
euc now=ans(a,b,c,n);
printf("%lld %lld %lld\n",now.f,now.h,now.g);
}
}