线性筛:
所有积性函数都能使用线性筛,线性筛保证每个数只会被它的最小质因子给筛掉,所以时间是线性的。
线性筛质数:
void init(int n) { for(ri i=2;i<=n;++i){ if(!pri[i]) su[++cnt]=i; for(ri j=1;j<=cnt && su[j]*i<=n;++j){ int k=su[j]*i; pri[k]=1; if(i%su[j]==0) break; } } }
线性筛约数个数:
void init(int n) { for(ri i=2;i<=n;++i){ if(!pri[i]) su[++cnt]=i,num[i]=2,tot[i]=1;//当i为质数的时候 正约数的个数为 1与i for(ri j=1;j<=cnt && su[j]*i<=n;++j){ int k=su[j]*i; pri[k]=1; if(i%su[j]==0){ num[k]=num[i]/(tot[i]+1)*(tot[i]+2);//质因子个数从原来的tot+1 变成tot+2 tot[k]=tot[i]+1;//在i的基础上多了su[j]这个质数 break; }//积性函数的性质 else num[k]=num[i]*num[su[j]],tot[k]=1;//i*su[j]包含了su[j]这个质因子 tot从1开始重新统计贡献 } } }
线性筛约数和:
void init(int n) { for(ri i=2;i<=n;++i){ if(!pri[i]) su[++cnt]=i,sum1[i]=1+i,sum2[i]=1+i;//质数的约数只有1和它本身 for(ri j=1;j<=cnt&&su[j]*i<=n;++j){ int k=su[j]*i; pri[k]=1; if(i%su[j]==0){ sum1[k]=sum1[i]/sum2[i]*(sum2[i]*su[j]+1);//去掉原有的 乘上现在的 sum2[k]=sum2[i]*su[j]+1; break; } else{ sum1[k]=sum1[i]*(su[j]+1);//su[j]+1==sum1[su[j]] sum2[k]=1+su[j];//从下一个新的约数开始统计贡献 } } } }
线性筛莫比乌斯函数:
void init() { u[1]=1; for(int i=2;i<=maxn;i++){ if(!pri[i]) su[++cnt]=i,u[i]=-1; for(int j=1;j<=cnt&&su[j]*i<=maxn;j++){ if(i%su[j]==0){ pri[i*su[j]]=1; u[i*su[j]]=0; break; } pri[i*su[j]]=1; u[i*su[j]]=-u[i]; } } }
线性筛欧拉函数:
void init(int n) { for(ri i=2;i<=n;++i){ if(!pri[i]) su[++cnt]=i,ol[i]=i-1; for(ri j=1;j<=cnt&&su[j]*i<=n;++j){ pri[su[j]*i]=1; if(i%su[j]==0){ ol[su[j]*i]=ol[i]*su[j]; break; } else ol[su[j]*i]=ol[i]*(su[j]-1); } } }
线性筛好题
质数 && 约数:
1.求 l~r 内素数的个数:洛谷P1835 素数密度_NOI导刊2011提高(04)
数论里面,对于 l 和 r 很大,r-l 范围又很小的时候,都用到了用一个数组x[ i ]表示 i+l 的值,也就是把 l~r 这个区间对应到 0~l-1 中了。
同时也预处理了可能对答案做出贡献的值(一般是预处理根号内的素数)。
#include<bits/stdc++.h> using namespace std; #define N 1000005 #define ll long long #define ri register int int cnt=0,ans=0,pri[N],su[N],fl[N]; ll l,r; void suu(int n) { for(ri i=2;i<=n;++i){ if(!pri[i]) su[++cnt]=i; for(ri j=1;j<=cnt&&su[j]*i<=n;++j){ pri[su[j]*i]=1; if(i%su[j]==0) break; } } } int main() { scanf("%lld%lld",&l,&r); suu(sqrt(r)); for(int i=1;i<=cnt;++i){ for(ll j=l/su[i]*su[i];j<=r;j+=su[i]){ if(j>=l && j!=su[i]) fl[j-l]=1; } } for(ll i=l;i<=r;++i) if(fl[i-l]==0) ans++; printf("%d ",ans); } /* 11 */ 筛大素数
2.求A^B所有约数和(A,B<=5e7):洛谷P1593 因子和
3.求1~n范围里约数的约数的个数加起来最多的是哪个数 及其总数
题解4.给定n和m,求Σ(1<=i<=n) Σ(1<=j<=m) GCD(i,j)*2-1 :洛谷P1447 [NOI2010]能量采集
/* 来源:洛谷第一篇题解 题目大意:给定n和m,求Σ(1<=i<=n)Σ(1<=j<=m)GCD(i,j)*2-1 i和j的限制不同,传统的线性筛法失效了,这里我们考虑容斥原理 令f[x]为GCD(i,j)=x的数对(i,j)的个数,这个不是很好求 我们令g[x]为存在公因数=x的数对(i,j)的个数(注意不是最大公因数!),显然有g[x]=(n/x)*(m/x) 但是这些数对中有一些的最大公因数为2d,3d,4d,我们要把他们减掉 于是最终f[x]=(n/x)*(m/x)-Σ(2*x<=i*x<=min(m,n))f[i*x] 从后向前枚举x即可 时间复杂度O(nlogn) */ #include<bits/stdc++.h> using namespace std; #define ll long long #define N 200005 ll n,m,f[N],ans=0; int main() { scanf("%lld%lld",&n,&m); ll minn=min(n,m); for(ll i=minn;i>=1;--i){ //计算n,m中有多少对公约数为 i的(注意不是最大公约数) f[i]=(n/i)*(m/i); //减去公约数为i*2 i*3 ...的 for(ll j=i*2;j<=minn;j+=i) f[i]-=f[j]; //根据题意统计答案 ans+=f[i]*(2*i-1); } printf("%lld ",ans); } /* 5 4 */
BSGS:
(没有做到不是模板的BSGS)
中国剩余定理:
1.模板:洛谷P3868 [TJOI2009]猜数字
(注意有坑)
3.综合运用:P2480 [SDOI2010]古代猪文
#include<bits/stdc++.h> using namespace std; #define Mod 999911659 #define ll long long #define ri register int #define N 40005 ll p[5]={0,2,3,4679,35617},fac[N],xx[5]; ll quick_pow(ll a,ll k,ll mod) { ll ans=1; while(k){ if(k&1) ans=ans*a%mod; a=a*a%mod; k>>=1; } return ans; } void init(ll n) { fac[0]=1; for(ll i=1;i<=n;++i) fac[i]=fac[i-1]*i %n; } ll C(ll n,ll m,ll mod) { if(n<m) return 0; return fac[n]* quick_pow(fac[n-m],mod-2,mod) %mod *quick_pow(fac[m],mod-2,mod) %mod; } ll lucas(ll n,ll m,ll mod) { if(n<m) return 0; if(m==0 || n==0) return 1; return lucas(n/mod,m/mod,mod) * C(n%mod,m%mod,mod) %mod; } void work1(ll n) { for(ri i=1;i<=4;++i){ init(p[i]); for(ri j=1;j*j<=n;++j) if(n%j==0){ xx[i]=( xx[i] + lucas(n,j,p[i]) ) %p[i]; if(j*j!=n) xx[i]=( xx[i] + lucas(n,n/j,p[i]) ) %p[i]; } } } ll exgcd(ll a,ll b,ll &x,ll &y) { if(!b) { x=1; y=0; return a; } ll d=exgcd(b,a%b,x,y); ll z=x; x=y; y=z-(a/b)*y; return d; } /*ll work2()中国剩余定理用扩欧求会TLE。。。 { ll M=Mod-1,ans=0; for(ri i=1;i<=4;++i){ ll mi=M/p[i],a=mi,b=p[i]; ll x,y,d=exgcd(a,b,x,y); ans=( ans + x*mi %M *xx[i] %M ) %M; } return ans; }*/ ll work2() { ll ans=0,M=Mod-1;//直接用逆元解同余方程 for(ri i=1;i<=4;++i) ans=( ans+ quick_pow(M/p[i],p[i]-2,p[i]) %M *xx[i] %M *(M/p[i]) %M) %M; return ans; } int main() { ll q,n; scanf("%lld%lld",&n,&q); if(q==Mod) { printf("0 "); return 0; } work1(n); printf("%lld ",quick_pow(q,work2(),Mod)); } /* 4 2 237573745 375736583 */
高斯消元:
矩阵乘法:
欧拉函数 && 欧拉定理:
exgcd:
1.P1516 青蛙的约会 :列式子求exgcd即可(注意判负数)
#include<bits/stdc++.h> using namespace std; #define ll long long ll exgcd(ll a,ll b,ll &x,ll &y) { if(!b) { x=1; y=0; return a; } ll d=exgcd(b,a%b,x,y); ll z=x; x=y; y=z-(a/b)*y; return d; } int main() { ll xx,yy,m,n,L; scanf("%lld%lld%lld%lld%lld",&xx,&yy,&m,&n,&L); ll a=m-n,b=L,c=yy-xx; //if(a<0) a=-a,c=-c; ll x,y,d=exgcd(a,b,x,y); //printf("%lld %lld %lld %lld =%lld ",a,x,b,y,d); if(c%d) printf("Impossible "); else{ x=( (c/d)*x %(abs(b/d)) +abs(b/d) ) %abs(b/d);//mod一定是正数!! 否则mod出来也会是负数 printf("%lld ",x); } return 0; }
2.[NOI2002]荒岛野人 :枚举模数,用exgcd检查即可。
#include<bits/stdc++.h> using namespace std; #define N 100005 int n,c[N],p[N],l[N]; int exgcd(int a,int b,int &x,int &y) { if(b==0) { x=1; y=0; return a; } int d=exgcd(b,a%b,y,x); y=y-(a/b)*x; return d; } bool check(int m) { for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { int a=p[i]-p[j],b=m,cc=c[j]-c[i],x,y; int d=exgcd(a,b,x,y); if(cc%d) continue;//无解成立 //x是相遇的最小整数天数 如果大于寿命则成立 //c%d==0 所以对应的x应该为x*(c/d); b=abs(b/d); x=(x*(cc/d)%b+b)%b; if(x<=l[i]&&x<=l[j]) return 0;//会相遇就直接判m不成立 } return 1; } int main() { int mx=0; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&c[i],&p[i],&l[i]),mx=max(c[i],mx); for(int i=mx;;i++) if(check(i)) return printf("%d ",i),0; } /* 3 1 3 4 2 7 3 3 2 1 ans 6 */
3.求解方程的正整数解个数,及和:
#include<bits/stdc++.h> using namespace std; #define ll long long ll gcd(ll a,ll b) { return b ? gcd(b,a%b) : a; } ll exgcd(ll a,ll b,ll &x,ll &y) { if(!b) { x=1; y=0; return a; } ll d=exgcd(b,a%b,x,y); ll z=x; x=y; y=z-(a/b)*y; return d; } int main() { freopen("pay.in","r",stdin); freopen("pay.out","w",stdout); int T,opt; scanf("%d%d",&T,&opt); while(T--){ ll a,b,c; scanf("%lld%lld%lld",&a,&b,&c); ll x,y,d=exgcd(a,b,x,y); if(c%d){ if(opt==1) printf("0 "); else printf("0 0 "); continue; } x=( (c/d)*x %(b/d) + b/d ) % (b/d);//把x变成最小非负整数解 b/d是x的增量 y=( c-a*x )/b; if(y<0){//如果说x的最小非负整数解y都小于0了 说明只有一个为负 一个为正的解 不符合题意 if(opt==1) printf("0 "); else printf("0 0 "); continue; } ll ans1=0,ans2=0; /*while(y>=0){//暴力跳解 其实只需要求一下等差序列的和即可 if(x>=0 && y>=0) ans1++,ans2+=x+y; x+=b/d; y-=a/d; }*/ int x1=y/(a/d),x2=y%(a/d);//推公式求等差序列 ans2+=(a/d)*(x1+1)*x1/2 + (x1+1)*x2; ans2+=(b/d)*(x1+1)*x1/2 + (x1+1)*x; ans1=x1+1;//推式子 if(opt==1) printf("%lld ",ans1); else printf("%lld %lld ",ans1,ans2); } } /* 2 2 6 9 3 3 4 21 2 4 12 */
注意x的增量是:b/d,通过求特解来求出所有满足情况的解。
逆元:
排列组合:
1. 组合数的常用公式大全
2.P4071 [SDOI2016]排列计数 :错排递推+组合数
//luogu4071 #include<bits/stdc++.h> using namespace std; #define ll long long #define ri register int #define N 1000005 const ll mod=1e9+7; ll f[N],invfac[N],fac[N]; ll C(int n,int m) { return fac[n]*invfac[n-m] %mod *invfac[m] %mod; } ll quick_pow(ll a,ll k) { ll ans=1; while(k){ if(k&1) ans=ans*a%mod; a=a*a%mod; k>>=1; } return ans; } void init() { int nn=1e6; f[0]=1; f[1]=0; f[2]=1; for(ri i=3;i<=nn;++i) f[i]=(i-1)*( ( f[i-1]+f[i-2]) %mod ) %mod; fac[0]=1; for(ri i=1;i<=nn;++i) fac[i]=fac[i-1]*i %mod; invfac[nn]=quick_pow(fac[nn],mod-2); for(ri i=nn;i>=1;--i) invfac[i-1]=invfac[i]*i %mod; } int main() { int T,n,m; scanf("%d",&T); init(); while(T--){ scanf("%d%d",&n,&m); if(n<m) printf("0 "); else printf("%lld ",f[n-m]*C(n,m)%mod); } }
3.problem 题(Catlan + dp + 组合数)
4.组合数插板法学习:(转载自here)
/*排列组合插板法小结 插板法就是在n个元素间的(n-1)个空中插入若干个(b)个板,可以把n个元素分成(b+1)组的方法。 应用插板法必须满足三个条件: (1)这n个元素必须互不相异 (2)所分成的每一组至少分得一个元素 (3) 分成的组别彼此相异 举个很普通的例子来说明 把10个相同的小球放入3个不同的箱子,每个箱子至少一个,问有几种情况? 问题的题干满足条件(1)(2),适用插板法,c9 2=36 下面通过几道题目介绍下插板法的应用 =================================================== a 凑元素插板法(有些题目满足条件(1),不满足条件(2),此时可适用此方法) 例1 :把10个相同的小球放入3个不同的箱子,问有几种情况? 3个箱子都可能取到空球,条件(2)不满足,此时如果在3个箱子种各预先放入 1个小球,则问题就等价于把13个相同小球放入3个不同箱子,每个箱子至少一个,有几种情况? 显然就是c12 2=66 ------------------------------------------------- 例2:把10个相同小球放入3个不同箱子,第一个箱子至少1个,第二个箱子至少3个,第三个箱子可以放空球,有几种情况? 我们可以在第二个箱子先放入10个小球中的2个,小球剩8个放3个箱子,然后在第三个箱子放入8个小球之外的1个小球,则问题转化为把9个相同小球放3不同箱子,每箱至少1个,几种方法?c8 2=28 ================================================== b 添板插板法 例3:把10个相同小球放入3个不同的箱子,问有几种情况? -o - o - o - o - o - o - o - o - o - o - o表示10个小球,-表示空位 11个空位中取2个加入2块板,第一组和第三组可以取到空的情况,第2组始终不能取空 此时若在第11个空位后加入第12块板,设取到该板时,第二组取球为空 则每一组都可能取球为空c12 2=66 -------------------------------------------------------- 例4:有一类自然数,从第三个数字开始,每个数字都恰好是它前面两个数字之和,直至不能再写为止,如257,1459等等,这类数共有几个? 因为前2位数字唯一对应了符合要求的一个数,只要求出前2位有几种情况即可,设前两位为ab 显然a+b<=9 ,且a不为0 1 -1- 1 -1 -1 -1 -1 -1 -1 - - 1代表9个1,-代表10个空位 插一个板使得b可以为0 我们可以在这9个空位中插入2个板,分成3组,第一组取到a个1,第二组取到b个1,但此时第二组始终不能取空,若多添加第10个空时,设取到该板时第二组取空,即b=0,所以一共有c10 2=45 ----------------------------------------------------------- 例5:有一类自然数,从第四个数字开始,每个数字都恰好是它前面三个数字之和,直至不能再写为止,如2349,1427等等,这类数共有几个? 类似的,某数的前三位为abc,a+b+c<=9,a,b不为0 1 -1- 1 -1 -1 -1 -1 -1 -1 - - - 这道题只需要保证a不为0 但是插3个板会使b,c不为0 而这道题b,c可以为0 所以插两个板表示它们取空的情况 在9个空位中插入3板,分成4组,第一组取a个1,第二组取b个1,第三组取c个1,由于第二,第三组都不能取到空,所以添加2块板 设取到第10个板时,第二组取空,即b=0;取到第11个板时,第三组取空,即c=0。所以一共有c11 3=165 ============================================ c 选板法 例6:有10粒糖,如果每天至少吃一粒(多不限),吃完为止,求有多少种不同吃法? o - o - o - o - o - o - o - o - o - o o代表10个糖,-代表9块板 10块糖,9个空,插入9块板,每个板都可以选择放或是不放,相邻两个板间的糖一天吃掉 这样一共就是2^9= 512啦 ============================================= d 分类插板 例7:小梅有15块糖,如果每天至少吃3块,吃完为止,那么共有多少种不同的吃法? 此问题不能用插板法的原因在于没有规定一定要吃几天,因此我们需要对吃的天数进行分类讨论 最多吃5天,最少吃1天 1:吃1天或是5天,各一种吃法一共2种情况 2:吃2天,每天预先吃2块,即问11块糖,每天至少吃1块,吃2天,几种情况?c10 1=10 3:吃3天,每天预先吃2块,即问9块糖,每天至少1块,吃3天? c8 2=28 4:吃4天,每天预先吃2块,即问7块糖,每天至少1块,吃4天?c6 3=20 所以一共是2+10+28+20=60 种 ================================= e 二次插板法 例8 :在一张节目单中原有6个节目,若保持这些节目相对次序不变,再添加3个节目,共有几种情况? -o - o - o - o - o - o - 三个节目abc 三个一起不好处理 可以分成一个一个地处理 可以用一个节目去插7个空位,再用第二个节目去插8个空位,用最后个节目去插9个空位 所以一共是c7 1×c8 1×c9 1=504种 -----------------------------------------------------------*/
待更新。。。