浅谈 OI 中的数论

· · 算法·理论

一些记号

$\gcd(a,b)$ 表示 $a,b$ 的最大公约数。 $[x=k]$ 表示函数 $f(x)=\begin{cases}1&x=k\\0&x\ne k\end{cases}

素数的基本知识

素数定义

因数个数为 2 的数为素数(质数)。

素数判定

朴素判定

也可以先打素数表,单次查询可降至 $O(\frac{\sqrt n}{\ln n})$。 #### Miller-Rabin 素数测试 对于素数 $p>2$ 而言,若 $x^2\equiv 1\pmod p$,则有 $x\equiv \pm1\pmod p$。 证明:等价于 $p|(x-1)(x+1)$,而 $p>(x+1)-(x-1)\ge \gcd(x+1,x-1)$,故只能 $p|x-1$ 或 $p|x+1$。 我们知道 $a^{p-1}\equiv1\pmod p$,可将 $p-1$ 分解为 $2^kq$ 的形式,其中 $q$ 为奇数,先计算 $a^q$,然后不断平方,看它是否由 $-1$ 变为 $1$。 [模板题](https://loj.ac/p/143)代码实现(`long long` 范围内 $100\%$ 正确): ::::info[Code] ```cpp #include<bits/stdc++.h> using namespace std; long long d[]={2,3,5,7,11,13,17,19,23,29,31,37}; long long pw(long long a,long long b,long long p){ long long res=1; while(b){ if(b&1) res=(__int128)(res)*a%p; a=(__int128)(a)*a%p; b>>=1; } return res; } bool is_prime(long long x){ for(long long i=0;i<12;i++){ long long u=d[i]; if(x==u) return true; if(x<u) return false; if(pw(u,x-1,x)!=1) return false; long long pos=x-1; while(!(pos&1)) pos>>=1; long long now=pw(u,pos,x); while(pos!=x-1){ if(now==x-1) break; if(now==1) break; if((__int128)(now)*now%x==1) return false; now=(__int128)(now)*now%x; pos<<=1; } } return true; } signed main(){ long long p; while(cin>>p){ cout<<(is_prime(p)?'Y':'N')<<'\n'; } return 0; } ``` :::: ## Lucas 定理、exLucas 定理 ### Lucas $$\Large C_{n}^{m}\equiv C_{[n/p]}^{[m/p]}C_{n\bmod p}^{m\bmod p}\pmod p$$ 这里认为 $m>n\Rightarrow C_n^m=0$。 ### exLucas 假如模数不是质数怎么办? 考虑质因数分解,只需处理模数为素数幂次的问题。 预处理每个数阶乘,将其表示为 $p^kq$ 的形式,其中 $\gcd(p,q)=1$,后略,可参考 [oi-wiki](https://oi-wiki.org/math/number-theory/lucas/#exlucas-%E7%AE%97%E6%B3%95)。 [exLucas 模板题](https://www.luogu.com.cn/problem/P4720)代码: ::::info[Code] ```cpp #include<bits/stdc++.h> using namespace std; typedef __int128 LL; namespace io{ inline __int128 read(){ __int128 ans=0,f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-f; c=getchar(); } while(c>='0'&&c<='9'){ ans=(ans<<3)+(ans<<1)+c-48; c=getchar(); } return ans*f; } inline char readchar(){ bk: char c=getchar(); if(c>32&&c<127) return c; if(c==EOF) return 0; goto bk; } void write(__int128 x){ if(x<0){ putchar('-'); x=-x; } if(x<10) putchar(x+48); else{ write(x/10); putchar(x%10+48); } } } namespace exLucas{ LL gcd(LL x,LL y){ if(y==0) return x; return gcd(y,x%y); } LL pw(LL a,LL b,LL p=0x3f3f3f3f3f3f3f3f){ LL res=1,now=a; while(b){ if(b&1) res=res*now%p; now=now*now%p; b>>=1; } return res; } LL mod(LL a,LL b,LL m){ if(m==0) exit(3); a%=m,b%=m; if(a==1) return b; if(a==0&&b==0) return 0; if(a==0) exit(4); LL k=mod((a-1)*m,b,a); return ((b+m*k)/a)%m; } LL excrt(vector<LL>a,vector<LL>b){ LL xa=0,xb=1; LL pos=0; while(pos!=a.size()){ LL na,nb,ee; na=a[pos++]; nb=b[pos-1]; LL xx=xb,yy=((na-xa)%nb+nb)%nb,zz=nb; ee=gcd(xx,zz); if(ee==0) exit(2); if(yy%ee!=0){ exit(1); } xx/=ee; yy/=ee; zz/=ee; LL k=mod(xx,yy,zz); xa=xa+xb*k; xb*=nb/gcd(xb,nb); xa%=xb; } return xa; } typedef pair<LL,LL>P; vector<P>t; LL f(LL n,LL p){ if(n<p) return 0; return n/p+f(n/p,p); } LL g(LL n,LL p,LL k){ if(n==0) return 1; LL t=pw(p,k),now=1; for(LL i=1;i<=t;i++){ LL j=i; if(j%p!=0) now=now*j%t; } now=pw(now,n/t,t); now=now*g(n/p,p,k)%t; for(LL i=1;i<=n%t;i++){ LL j=i; if(j%p!=0) now=now*j%t; } assert(now); return now; } LL C(LL x,LL y,LL p,LL k){ LL t=f(x,p)-f(y,p)-f(x-y,p); if(t>=k) return 0; k-=t; LL tmp=pw(p,k); assert(tmp); LL ans=(g(x,p,k)*mod(g(y,p,k),1,tmp)%tmp*mod(g(x-y,p,k),1,tmp)%tmp)*pw(p,t); return ans; } LL exlucas(LL n,LL m,LL p){ t.clear(); LL tmp=p; for(LL i=2;i*i<=tmp;i++){ LL cnt=0; while(tmp%i==0){ tmp/=i; cnt++; } if(cnt) t.push_back(P(i,cnt)); } if(tmp!=1) t.push_back(P(tmp,1)); vector<LL>a,b; for(LL i=0;i<t.size();i++){ LL u=t[i].first,v=t[i].second; a.push_back(C(n,m,u,v)),b.push_back(pw(u,v)); } return excrt(a,b); } } signed main(){ LL n=io::read(),m=io::read(),p=io::read(); io::write(exLucas::exlucas(n,m,p)); putchar(10); return 0; } ``` :::: ## 一些数论函数 ### 定义 $1(x)$ 表示函数值恒为 $1$ 的函数。 $d(x)$ 表示 $x$ 的正因数个数。 $\varepsilon (x)=[x=1]$。 $\mu(x)=\begin{cases}1&x\ \text{的质因子个数为偶数且互不相同}\\0&x\ \text{存在平方因子}\\-1&x\ \text{的质因子个数为奇数且互不相同}\end{cases} $\varphi(x)$ 表示 $1$ 到 $x$ 中与 $x$ 互质的数的个数。 ### 举例 以防你看不懂,下面列举部分函数值: ::::info[表格] | $x$ | $1(x)$ | $\varepsilon(x)$ | $\mu(x)$ | $id(x)$ | $\varphi(x)$ |$d(x)$| |:-:|:-:|:-:|:-:|:-:|:-:|:-:| | $1$ | $1$ | $1$ | $1$ | $1$ | $1$ | $1$ | | $2$ | $1$ | $0$ | $-1$ | $2$ | $1$ | $2$ | | $3$ | $1$ | $0$ | $-1$ | $3$ | $2$ | $2$ | | $4$ | $1$ | $0$ | $0$ | $4$ | $2$ | $3$ | | $5$ | $1$ | $0$ | $-1$ | $5$ | $4$ | $2$ | | $6$ | $1$ | $0$ | $1$ | $6$ | $2$ | $4$ | | $7$ | $1$ | $0$ | $-1$ | $7$ | $6$ | $2$ | | $8$ | $1$ | $0$ | $0$ | $8$ | $4$ | $4$ | | $9$ | $1$ | $0$ | $0$ | $9$ | $6$ | $3$ | | $10$ | $1$ | $0$ | $1$ | $10$ | $4$ | $4$ | :::: ### 性质 如果对于任意**互质**的正整数 $x,y$,有 $f(x)f(y)=f(xy)$,称 $f$ 为积性函数。 如果对于任意正整数 $x,y$,有 $f(x)f(y)=f(xy)$,称 $f$ 为完全积性函数。 上述函数均为积性函数,其中 $1(x),\varepsilon(x),id(x)$ 为完全积性函数。 ## 莫比乌斯反演 ### 狄利克雷卷积定义 若有 $$f(x)=\sum_{d|x}g(d)h(\frac{x}{d})$$ 恒成立,则认为 $f$ 为 $g$ 与 $h$ 的卷积,记作 $f=g\ast h$。 ### 重要性质 $f,g,h$ 中有任意两个为积性函数,可推出第三个亦为积性函数。 $$\mu\ast1=\varepsilon$$ $$1\ast1=d$$ $$1\ast\varphi=id$$ $$d(xy)=\sum_{i|x}\sum_{j|y}[\gcd(i,j)=1]$$ ## 筛法 ### 素数筛 $O(n\log\log n)$ 的埃氏筛和 $O(n)$ 的欧拉筛,想必大佬们都会。 ### 杜教筛 $O(n^{\frac 23})$ 求积性函数前缀和。 $$f\ast g=h$$ $$F(n)=\sum_{i=1}^nf(i)$$ $$G(n)=\sum_{i=1}^ng(i)$$ $$H(n)=\sum_{i=1}^nh(i)$$ $$g(1)F(n)=g(1)\sum_{i=1}^nf(i)\\=\sum_{i=1}^nh(i)-\sum_{i=2}^nF([\frac ni])g(i)$$ 整除分块。 预处理可平衡复杂度至 $O(n^\frac 23)$。 [模板题](https://www.luogu.com.cn/problem/P4213)代码: ::::info[Code] ```cpp #include<bits/stdc++.h> using namespace std; long long sumI(long long l,long long r){ return r-l+1; } long long sumid(long long l,long long r){ return (l+r)*(r-l+1)/2; } long long sumx(long long l,long long r){ return l<=1&&r>=1; } long long hashh(long long t){ return t%47224349; } long long mp[50000005]; long long a[15],b[15],c[15]; long long s[1500005],t[1500005],pf[1500005],u[1500005]; long long pr[100005],cnt; long long solve(long long n,long long (*g)(long long l,long long r),long long(*h)(long long l,long long r)){//f*g=h long long w=hashh(n); if(n<=1500000) return h(159,159)==159?t[n]:u[n]; if(mp[w]) return mp[w]; mp[w]=h(1,n); long long u=1; for(long long i=2;i*i<=n;i++){ u=i; mp[w]-=g(i,i)*solve(n/i,g,h); } for(long long i=1;i<=u;i++){ long long l=n/(i+1)+1,r=n/i; if(l<=u) l=u+1; mp[w]-=g(l,r)*solve(i,g,h); } return mp[w]; } void del(long long n){//f*g=h long long w=hashh(n); if(!mp[w]) return; mp[w]=0; long long u=1; for(long long i=2;i*i<=n;i++){ u=i; del(n/i); del(i); } return ; } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); for(long long i=2;i<=1500000;i++){ if(!s[i]){ pr[++cnt]=i; s[i]=i; long long now=i; while(now<=1500000){ t[now]=now/i*(i-1); now*=i; } } if(i/s[i]%s[i]==0) pf[i]=1; for(long long j=1;j<=cnt;j++){ if(i*pr[j]>1500000) break; s[i*pr[j]]=pr[j]; if(s[i]==pr[j]) break; } } for(long long i=2;i<=1500000;i++){ if(t[i]) continue; long long u=s[i],v=i/s[i]; while(v%s[i]==0){ u*=s[i]; v/=s[i]; } t[i]=t[u]*t[v]; } u[1]=1; t[1]=1; for(long long i=2;i<=1500000;i++){ if(!pf[i]) u[i]=-u[i/s[i]]; else u[i]=0; } // for(long long i=1;i<=100;i++){ // cout<<i<<' '<<s[i]<<' '<<t[i]<<' '<<u[i]<<'\n'; // } for(long long i=2;i<=1500000;i++){ u[i]+=u[i-1]; t[i]+=t[i-1]; } // exit(0); long long t; cin>>t; for(long long i=1;i<=t;i++) cin>>a[i]; for(long long i=1;i<=t;i++){ b[i]=solve(a[i],sumI,sumid); } // memset(mp,0,sizeof(mp)); for(long long i=1;i<=t;i++) del(a[i]); // for(long long i=1;i<=50000;i++) solve(i,sumI,sumx); for(long long i=1;i<=t;i++){ c[i]=solve(a[i],sumI,sumx); } for(long long i=1;i<=t;i++){ cout<<b[i]<<' '<<c[i]<<'\n'; } /* while(t--){ cin>>n; cout<<solve(n,sumI,sumid)<<' '; del(n); cout<<solve(n,sumI,sumx)<<'\n'; del(n); } */ return 0; } /* n/l<i+1 n/(l-1)>=i+1 n/(i+1)<l n/(i+1)>=l-1 */ ``` :::: ### PN 筛 定义形如 $a^2b^3$ 的数为 Powerful Number(即不含一次素因子的数)。 结论:PN 个数为 $O(\sqrt n)$。 想求 $\sum_{i=1}^n f(i)$,构造 $g$ 使得 $\forall p\in prime$ 有 $f(p)=g(p)$,构造 $h\ast g=f$,则有 $h$ 为积性函数,且 $\forall i\notin PN,h(i)=0$,构造 $G(u)=\sum_{i=1}^ug(i)$。 $$\large\sum_{i=1}^nf(i)=\sum_{i=1}^n\sum_{d|i}g(d)h(\frac id)\\=\sum_{i=1}^n\sum_{j=1}^{\frac ni}g(j)h(i)\\=\sum_{i=1,i\in PN}^nh(i)G(\frac ni)$$ ## 例题选讲 ### [P2257](https://www.luogu.com.cn/problem/P2257) #### 题面 计算: $$\large \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=prime]$$ #### 解法 莫比乌斯反演,下面式子中 $p$ 均为质数。 $$\large \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=prime]\\=\sum_{p=2}^{\min(n,m)}\sum_{i=1}^{\frac np}\sum_{j=1}^{\frac mp}[\gcd(i,j)=1]\\=\sum_{p=2}^{\min(n,m)}\sum_{i=1}^{\frac np}\sum_{j=1}^{\frac mp}\sum_{k|i,k|j}\mu(k)\\=\sum_{p=2}^{\min(n,m)}\sum_{k=1}^{\frac{\min(n,m)}p}[\frac n{pk}][\frac m{pk}]\mu(k)\\=\sum_{T=2}^{\min(n,m)}\sum_{p|T}[\frac nT][\frac mT]\mu(\frac Tp)\\=\sum_{T=2}^{\min(n,m)}a_T[\frac nT][\frac mT]$$ 整除分块即可,$O(T\sqrt{\max(n,m)})$。 预处理 $a_T=\sum_{p|T}\mu(\frac Tp)$, $O(n\log\log n)$。 本题轻微卡常。 ::::info[Code] ```cpp #include<bits/stdc++.h> using namespace std; long long a[10000005]; long long p[10000005]; long long sum[10000005]; long long mu[10000005]; signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); mu[1]=1; for(long long i=2;i<=10000000;i++){ if(a[i]==0){ mu[i]=-1; p[i]=i; for(long long j=i*i;j<=10000000;j+=i){ p[j]=i; a[j]++; } } } for(long long i=2;i<=10000000;i++){ a[i]=0; if(!mu[i]){ if(i/p[i]%p[i]==0) continue; mu[i]=-mu[i/p[i]]; } } for(long long i=1;i<=10000000;i++){ if(p[i]==i){ for(long long j=i;j<=10000000;j+=i){ a[j]+=mu[j/i]; } } sum[i]=sum[i-1]+a[i]; } long long t; cin>>t; while(t--){ long long n,m,ans=0; cin>>n>>m; for(long long i=2;i<=n&&i<=m&&i<=2000;i++) ans+=(n/i)*(m/i)*a[i]; long long l=2001,r; while(l<=n&&l<=m){ r=min(n/(n/l),m/(m/l)); ans+=(n/l)*(m/l)*(sum[r]-sum[l-1]); l=r+1; } cout<<ans<<'\n'; } return 0; } ``` :::: ### [P4240](https://www.luogu.com.cn/problem/P4240) #### 题面 $T$ 次询问,每次给定 $n, m$,Salamander 需要回答出 $\left( \sum_{i=1}^n \sum_{j=1}^m \varphi(ij) \right)\! \bmod 998244353$。 对于 $100\%$ 的数据,$1 \le T \le {10}^4$,$1 \le n, m \le {10}^5$。 #### 解法 $$\varphi(xy)=\varphi(x)\varphi(y)\frac{\gcd(x,y)}{\varphi(\gcd(x,y))}$$ $$\large\sum_{i=1}^n \sum_{j=1}^m \varphi(ij)=\sum_{x=1}^n\sum_{y=1}^m\varphi(x)\varphi(y)\frac{\gcd(x,y)}{\varphi(\gcd(x,y))}\\=\sum_{T=1}^{\min(n,m)}\frac T{\varphi(T)}\sum_{i=1}^\frac nT\sum_{j=1}^\frac mT\varphi(iT)\varphi(jT)[\gcd(i,j)=1]\\=\sum_{T=1}^{\min(n,m)}\frac T{\varphi(T)}\sum_{t=1}^\frac nT\mu(t)\sum_{i=1}^\frac n{Tt}\varphi(iTt)\sum_{j=1}^\frac m{Tt}\varphi(jTt)\\=\sum_{k=1}^{\min(n,m)}\sum_{i=1}^{\frac{n}{k}}\varphi(ik)\sum_{j=1}^{\frac{m}{k}}\varphi(jk)\sum_{d|k}\frac{d\mu(\frac kd)}{\varphi(d)}$$ 记 $\large a_k=\sum_{d|k}\frac{d\mu(\frac kd)}{\varphi(d)}$(可 $O(n\log n)$ 预处理),$\large b_{t,w}=\sum_{i=1}^{t}\varphi(wi)$,则即求: $$\large\sum_{k=1}^{\min(n,m)}b_{\frac nk,k}b_{\frac mk,k}a_k$$ 这个式子 $b$ 里面带 $k$,不能整除分块,我们令 $$\large t_{x,y,z}=\sum_{i=1}^xa_ib_{y,i}b_{z,i}$$ 根号分治,后略。 ::::info[Code] ```cpp #include<bits/stdc++.h> using namespace std; const long long p=998244353; const long long B=18; long long phi[100005],mu[100005]; long long a[100005],sum[100005]; vector<long long>b[100005]; long long t[100005][20][20]; long long pw(long long a,long long b=998244351,long long p=998244353){ long long res=1; while(b){ if(b&1) res=res*a%p; a=a*a%p; b>>=1; } return res; } signed main(){ //O(nlogn) 筛出phi,mu for(long long i=1;i<=100000;i++) phi[i]=i; mu[1]=1; for(long long i=1;i<=100000;i++){ for(long long j=i+i;j<=100000;j+=i){ mu[j]-=mu[i]; phi[j]-=phi[i]; } } for(long long i=1;i<=100000;i++){ for(long long j=i;j<=100000;j+=i){//i向j贡献 a[j]+=i*mu[j/i]*pw(phi[i]); a[j]%=p; } a[i]+=p; a[i]%=p; sum[i]=(sum[i-1]+a[i])%p; } for(long long i=1;i<=100000;i++) b[i].push_back(0); for(long long w=1;w<=100000;w++){ for(long long t=1;w*t<=100000;t++){ b[w].push_back((b[w][b[w].size()-1]+phi[w*t])%p); } } for(long long x=1;x<=100000;x++){ for(long long y=1;y<=B&&y*x<=100000;y++){ for(long long z=1;z<=B&&z*x<=100000;z++){ assert(b[y].size()>x); assert(b[z].size()>x); t[x][y][z]=t[x-1][y][z]+a[x]*b[x][y]%p*b[x][z]%p; t[x][y][z]%=p; } } } ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); long long q; cin>>q; while(q--){ long long n,m; cin>>n>>m; if(min(n,m)<5600){ long long ans=0; for(long long i=1;i<=min(n,m);i++){ ans+=a[i]*b[i][n/i]%p*b[i][m/i]%p; ans%=p; } cout<<ans<<'\n'; }else{ long long ans=0; for(long long i=1;i<=5600;i++){ ans+=a[i]*b[i][n/i]%p*b[i][m/i]%p; // cout<<a[i]*b[i][n/i]%p*b[i][m/i]<<'\n'; ans%=p; } long long l=5601,r; while(l<=n&&l<=m){ r=min(n/(n/l),m/(m/l)); ans+=t[r][n/l][m/l]-t[l-1][n/l][m/l]; // cout<<t[r][n/l][m/l]-t[l-1][n/l][m/l]<<'\n'; ans%=p; ans+=p; ans%=p; l=r+1; } cout<<ans<<'\n'; } } return 0; } ``` :::: ### [P3726](https://www.luogu.com.cn/problem/P3726) 记小 A 向上 $s$ 次,向下 $(a-s)$ 次,小 B 向上 $(b-t)$ 次,向下 $t$ 次,只需 $s+t>b$ 即可。 $$Ans=\sum_{i=b+1}^{a+b} C_{a+b}^i=\frac 12\sum_{i=b+1}^{a+b}C_{a+b}^{i}+\frac 12\sum_{i=0}^{a-1}C_{a+b}^i\\=\frac{2^{a+b}+\sum_{i=b+1}^{a-1}C_{a+b}^i}2$$ exLucas 求解即可。 喜提洛谷第四劣解,并喜提 $2$ 发 $95$ 分 $1.01s\ TLE$。 ::::info[Code] ```cpp #include<bits/stdc++.h> using namespace std; typedef __int128 LL; namespace exLucas{ LL id[]={0,4,5,8,16,25,32,64,125,128,256,512,625,1024,3125,15625,78125,390625,1953125}; LL len=18; vector<LL>a[20]; LL gcd(LL x,LL y){ if(y==0) return x; return gcd(y,x%y); } void init(){ for(LL i=1;i<=len;i++){ LL now=id[i]; a[i].resize(now+1); a[i][0]=1; for(LL j=1;j<=now;j++){ a[i][j]=a[i][j-1]*(now%5==0&&j%5==0||now%2==0&&j%2==0?1:j)%now; } } } LL pw(LL a,LL b,LL p=0x3f3f3f3f3f3f3f3f){ LL res=1,now=a; while(b){ if(b&1) res=res*now%p; now=now*now%p; b>>=1; } return res; } LL mod(LL a,LL b,LL m){ if(m==0) exit(3); a%=m,b%=m; if(a==1) return b; if(a==0&&b==0) return 0; if(a==0) exit(4); LL k=mod((a-1)*m,b,a); return ((b+m*k)/a)%m; } LL excrt(vector<LL>a,vector<LL>b){ LL xa=0,xb=1; LL pos=0; while(pos!=a.size()){ LL na,nb,ee; na=a[pos++]; nb=b[pos-1]; LL xx=xb,yy=((na-xa)%nb+nb)%nb,zz=nb; ee=gcd(xx,zz); if(ee==0) exit(2); if(yy%ee!=0){ exit(1); } xx/=ee; yy/=ee; zz/=ee; LL k=mod(xx,yy,zz); xa=xa+xb*k; xb*=nb/gcd(xb,nb); xa%=xb; } return xa; } typedef pair<LL,LL>P; vector<P>t; LL f(LL n,LL p){ if(n<p) return 0; return n/p+f(n/p,p); } LL g(LL n,LL p,LL k){ if(n==0) return 1; LL t=pw(p,k),now=1; // for(LL i=1;i<=t;i++){ // if(i%p!=0) now=now*i%t; // } LL d=lower_bound(id+1,id+1+len,t)-id; now=now*a[d][t]%t; now=pw(now,n/t,t); now=now*g(n/p,p,k)%t; // for(LL i=1;i<=n%t;i++){ // LL j=i; // if(j%p!=0) // now=now*j%t; // } now=now*a[d][n%t]%t; assert(now); return now; } LL C(LL x,LL y,LL p,LL k){ LL t=f(x,p)-f(y,p)-f(x-y,p); if(t>=k) return 0; k-=t; LL tmp=pw(p,k); assert(tmp); LL ans=(g(x,p,k)*mod(g(y,p,k),1,tmp)%tmp*mod(g(x-y,p,k),1,tmp)%tmp)*pw(p,t); return ans; } LL exlucas(LL n,LL m,LL p){ t.clear(); LL tmp=p; for(LL i=2;i*i<=tmp;i++){ LL cnt=0; while(tmp%i==0){ tmp/=i; cnt++; } if(cnt) t.push_back(P(i,cnt)); } if(tmp!=1) t.push_back(P(tmp,1)); vector<LL>a,b; for(LL i=0;i<t.size();i++){ LL u=t[i].first,v=t[i].second; a.push_back(C(n,m,u,v)),b.push_back(pw(u,v)); } return excrt(a,b); } } LL p[10]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000}; signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); exLucas::init(); long long a,b,k; while(cin>>a>>b>>k){ LL mod=p[k]*2; LL ans=exLucas::pw(2,a+b,mod); for(LL i=b+1;i<=a-1;i++){ ans+=exLucas::exlucas(a+b,i,mod); ans%=mod; } if(a==b) ans+=mod-exLucas::exlucas(a+b,b,mod); ans%=mod; ans/=2; for(LL i=k-1;~i;i--) cout<<(int)(ans/p[i]%10); cout<<'\n'; } return 0; } ``` :::: ### [P5325](https://www.luogu.com.cn/problem/P5325) Min_25 筛的板子题,复杂度 $O(\frac {n^{\frac 34}}{\log n})$,但可以用杜教筛和 PN 筛在 $O(n^{\frac 23})$ 时间内通过。 令 $g(x)=x\varphi(x),t(x)=x^2$,有 $id\ast g=t$。 易证 $p$ 为质数时 $f(x)=g(x)$,可以 PN 筛,求 $G(x)$ 部分用杜教筛。 此时推导可得 $h(p^k)=(k-1)(p-1)p^k$,后略。 ::::info[Code] ```cpp #include<bits/stdc++.h> using namespace std; const long long B=15000000; const long long mod=1000000007; const long long d2=500000004; const long long d6=166666668; //g(x)=x\varphi(x),g*id=id2 long long G[B+5];//预处理g函数前缀和 long long p[(B>>3)+5],cnt; long long s[B+5]; map<long long,long long>mp; long long getG(long long h){//杜教筛 if(h<=B) return G[h]; if(mp[h]) return mp[h]; long long ans=h%mod*((h+1)%mod)%mod*((h+h+1)%mod)%mod*d6%mod; long long l=2,r; while(l*l<=h){ ans-=getG(h/l)*l%mod; l++; } while(l<=h){ r=h/(h/l); ans-=getG(h/l)*((r-l+1)%mod)%mod*((l+r)%mod)%mod*d2%mod; l=r+1; } ans=(ans%mod+mod)%mod; return mp[h]=ans; } struct edge{ long long num,p; bool operator<(const edge &b)const{ return num<b.num; } bool operator==(const edge &b)const{ return num==b.num; } }; edge pn[1000005]; long long m;//Powerful Numbers long long h[1000005];//i=PN处的h值 long long solve(long long n){//PN筛 //先筛Powerful Number for(long long a=1;a*a<=n;a++){ for(long long b=1;a*a*b*b*b<=n;b++){ pn[++m]={a*a*b*b*b,s[max(a,b)]}; } } sort(pn+1,pn+m+1); m=unique(pn+1,pn+m+1)-pn-1; //再筛h for(long long i=1;i<=m;i++){ long long k=pn[i].num; if(k==1){ h[1]=1; } else{ long long p=pn[i].p; assert(~p); long long tmp=k; long long w=0; while(tmp%p==0){ tmp/=p; w++; } assert(w>=2); h[i]=h[lower_bound(pn+1,pn+m+1,(edge){tmp,0})-pn]*(w-1)*(p-1)*(k/tmp)%mod; } } long long ans=0; for(long long i=1;i<=m;i++){ ans+=h[i]*getG(n/pn[i].num); ans%=mod; } return ans; } signed main(){ //线性预处理g函数前缀和 G[1]=1; for(long long i=2;i<=B;i++){ if(!s[i]){ s[i]=i; p[++cnt]=i; } for(long long j=1;j<=cnt&&i*p[j]<=B;j++){ s[i*p[j]]=p[j]; if(i%p[j]==0) break; } } for(long long i=1;i<=cnt;i++){ long long d=p[i]; for(long long j=d;j<=B;j*=d){ G[j]=j/d*(d-1); } } for(long long i=2;i<=B;i++) if(!G[i]){ if(i/s[i]%s[i]==0) G[i]=G[i/s[i]]*s[i]; else G[i]=G[i/s[i]]*G[s[i]]; } for(long long i=1;i<=B;i++) G[i]*=i; for(long long i=1;i<=B;i++) G[i]%=mod; for(long long i=1;i<=B;i++) G[i]+=G[i-1]; for(long long i=1;i<=B;i++) G[i]%=mod; long long n; cin>>n; cout<<solve(n); return 0; } ``` ::::