一些容易忘的
素数在阶乘中的幂次:Legendre公式
另一种表示方法是 \(\frac{n-s_p(n)}{p-1}\) ,其中 \(s_p(n)\) 位 \(n\) 在 \(p\) 进制下的各位数字之和。
素数在组合数中的幂次:Kummer定理:\(v_p(\binom {n+m}m)\) 为 \(p\) 进制下 \(n+m\) 的进位次数。
exgcd 求得特解的数值范围是 \(|x|\le |\dfrac b{2d}|,|y|\le |\dfrac a{2d}|\) 。
若 \(a|b\),则 \(\varphi(a)|\varphi(b)\) 。
P4139 上帝与集合的正确用法
经典例题(幂塔)。
由于是无限叠的幂塔所以不需要考虑扩展欧拉定理中 \(b<\varphi(p)\) 的情况。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int getphi(int x){
int ans=x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
ans=ans/i*(i-1);
while(x%i==0)x/=i;
}
}
if(x!=1)ans=ans/x*(x-1);
return ans;
}
int qpow(int a,int b,int mod){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
int f(int x){
if(x<=2)return 0;
int ret=getphi(x);
return qpow(2,f(ret)+ret,x);
}
int main(){
int tim;scanf("%d",&tim);
while(tim--){
int x;scanf("%d",&x);
printf("%d\n",f(x));
}
return 0;
}
excrt。实际上crt和excrt的复杂度是一样的而且excrt更广泛。(然后考的时候发明失败保龄了)
考虑合并两个同余方程 \(x\equiv a_1\pmod {m_1},x\equiv a_2\pmod {m_2}\)。
\(x\) 可以表示为 \(a_1+pm_1\),代入第二个方程可以得到 \(a_1+pm_1\equiv a_2\pmod{m_2}\),也就是 \(pm_1\equiv a_2-a_1\pmod{m_2}\)。exgcd求解即可。最后的模数是 \(\text{lcm}(m_1,m_2)\)。
\(\binom nm\) 为奇数当且仅当 \(n\ \&\ m=m\)。也就是 lucas 定理在 \(\text{mod}=2\) 的情况。
\(n\) 个有度数要求 \(d_i\) 的节点的有标号无根树的个数为 \(\dfrac{(n-2)!}{\prod_{i=1}^n(d_i-1)!}\) 。用到的东西:度数为 \(d\) 的节点在prufer序列中出现 \(d-1\) 次,还有个多重集的排列数。
\(k\) 个大小为 \(a_i\) 的连通块,共 \(n\) 个节点的有标号无根树个数为 \(n^{k-2}\prod_{i=1}^ka_i\)。
积性函数只要在素数幂 \(p^k\) 处能 \(O(k)\) 求值就能线性筛。复杂度证明比较玄学,有会的吗。
P5495 Dirichlet 前缀和
大致原理是仿照高维前缀和,把每个质数看做一维做前缀和。这玩意有个实际意义就是数论函数卷上 \(1\) 。还有个东西叫 Dirichlet 差分,就是卷个 \(\mu\)。就是下面代码把 \(+\) 改成 \(-\) 。
其实也可以后缀和和后缀差分。复杂度 \(O(n\log\log n)\)。
#include <algorithm>
#include <iostream>
#include <cstdio>
#define int unsigned int
using namespace std;
int n,seed,a[20000010];
int p[2000010];
bool v[20000010];
void get(int n){
for(int i=2;i<=n;i++){
if(!v[i])p[++p[0]]=i;
for(int j=1;j<=p[0]&&i*p[j]<=n;j++){
v[i*p[j]]=true;
if(i%p[j]==0)break;
}
}
}
inline int getnext(){
seed^=seed<<13;
seed^=seed>>17;
seed^=seed<<5;
return seed;
}
signed main(){
scanf("%u%u",&n,&seed);
get(n);
for(int i=1;i<=n;i++)a[i]=getnext();
for(int i=1;i<=p[0];i++){
for(int j=1;p[i]*j<=n;j++){
a[p[i]*j]+=a[j];
}
}
int ans=0;
for(int i=1;i<=n;i++)ans^=a[i];
printf("%u\n",ans);
return 0;
}
对于 \(\forall i\in [1,n]\) ,不同的 \(\lfloor \dfrac ni \rfloor\) 至多 \(2\sqrt n\) 个。证明: \(i\le\sqrt n\) 时有根号个值, \(i>\sqrt n\) 时\(\lfloor \dfrac ni \rfloor\le \sqrt n\),也是根号个值。
向上取整的整除分块:\(r=\left\lfloor\dfrac{n-1}{\lfloor \frac nl\rfloor-1}\right\rfloor\)。特盘 \(\lceil \dfrac nl\rceil=1\)。
\(O(n\log n)\) 递推 \(\mu\):根据 \(\sum_{d|n}\mu(d)=[n=1]\)。
void get(int n){
miu[1]=1;
for(int i=1;i<=n;i++){
for(int j=(i<<1);j<=n;j+=i){
miu[j]-=miu[i];
}
}
}
\(\sum_{i=1}^n\mu^2(i)\) 相当于无平方因子数计数,容斥一下可以得到就是 \(\sum_{i=1}^{\sqrt n}\mu(i)\left\lfloor \dfrac n{i^2}\right\rfloor\)。