部分简单数论模板(未完待续)
来源:互联网 发布:wordpress seo插件 编辑:程序博客网 时间:2024/06/15 11:44
1.著名的欧几里得算法
附上一些gcd和lcm的简单性质:
lcm(S/a, S/b) = S/gcd(a, b)
int gcd(int a,int b){ return b==0?a:gcd(b,a%b);}
2.扩展欧几里得算法
int ex_gcd(int a,int b,int& x,int& y){ if(b==0){ x = 1, y = 0; return a; } int d = ex_gcd(b,a%b,y,x);//注意x,y互换位置 y -= a/b*x; return d; //返回值是a,b的GCD}
或者另一种实现
void ex_gcd(int a,int b,int &d,int &x,int &y){ if(b==0){ x = 1, y = 0, d = a; } else{ gcd(b,a%b,d,y,x); y -= a/b*x; }}
3.快速幂
long long pow(long long a,long long b){ long long ans = 1,base = a; while(b){ if(b&1) ans *= base; base *= base; b>>=1; } return ans;}
4.快速幂取模
long long pow_mod(long long a,long long b,long long mod){ a %= mod;//视情况而定,如果a*a可能爆long long,就加上 long long ans = 1,temp = a; while(b){ if(b&1) ans = (ans*temp) % mod; temp = temp*temp%mod; b /= 2; } return ans;}
5.线性时间素数筛
const int maxn = 1e6+10;bool vis[maxn];int prime_cnt;int prim[maxn/10+5];void Prime(){ prime_cnt = 0; memset(vis,0,sizeof(vis)); for(int i=2;i<maxn;i++){ if(vis[i]==0) prim[prime_cnt++] = i; for(int j=0;j<prime_cnt && i*prim[i]<maxn;j++){ vis[i*prim[i]] = 1; if(i % prim[i] == 0) break; } }}
6.区间素数筛
const int maxn = 1e5+10;bool vis[maxn];int prim[maxn];int cnt;void Prime(long long L,long long R){ long long len = R-L+1; memset(vis,0,sizeof(vis)); int flag = 0; if(L%2==1) flag++; for(int i=flag;i<len;i+=2) vis[i] = 1; int m = sqrt(R+0.5); for(int i=3;i<=m;i+=2){ if(i>L && vis[i-L] == true) continue; int j= (L/i)*i; if(j<L) j+=i; if(j==i) j+=i; j-=L; for(;j<len;j+=i) vis[j] = 1; } if(L==1) vis[0] = true; if(L<=2) vis[2-L] = false; cnt = 0; for(int i=0;i<len;i++) if(!vis[i]) prim[cnt++] = i+L;}
7.欧拉函数
//直接求法,复杂度O(sqrt(n))int phi(int n){ int ans = n,a = n; for(int i=2;i*i<a;i++){ while(a%i==0){ ans = ans/i*(i-1);//先进行除法防止中间数据溢出 while(a%i==0) a /= i; } } if(a>1) ans = ans/a*(a-1); return ans;}
求区间内所有值的欧拉函数值,可以使用筛法
//欧拉筛int euler[maxn];void Euler(){ for(int i=2;i<maxn;i++) euler[i] = i; for(int i = 2;i<maxn;i++) if(euler[i] == i) for(int j=i;j<maxn;j+=i) euler[j] = euler[j]/i*(i-1);}
8.逆元
扩展欧几里得求解逆元
int ex_gcd(int a,int b,int& x,int& y){ if(b==0){ x = 1,y = 0; return a; } int d = ex_gcd(b,b%a,y,x); y -= a/b*x; return d;}int inv(int a,int p)//不存在则返回-1{ int d,x,y; d = ex_gcd(a,p,x,y); return d==1?(x%p+p)%p:-1;}
费马小定理求解逆元
int inv(int a,int p){ return pow_mod(a,p-2,p);//或许会用到降幂公式}
若模数p为素数,可根据逆元的性质求解:inv(a) = (p - p / a) * inv(p % a) % p
//求a关于p的逆元,注意:a要小于p,最好传参前先把a%p一下int inv(int a,int p){ return a==1 ? 1 : (p-p/a)*inv(p%a,p)%p;}
运用刚刚提到的性质,可以在O(n)的时间内求出1~n的逆元
const long long n = 1e5+10;const long long Mod = 1e9+7;long long inv[n];void Inv(){ inv[1] = 1; for(int i=2;i<n;i++) inv[i] = (Mod-Mod/i)*inv[Mod%i]%Mod;}
9.中国剩余定理
模数互素时
long long CRT(int n,long long a[],long long m[]){ long long M = 1,ans = 0; for(int i=0;i<n;i++) M *= m[i]; for(int i=0;i<n;i++){ long long temp = M/m[i]; ans = (ans+temp*inv(temp,m[i])*a[i])%M; } return (ans+M)%M;}
模数不互素时
typedef long long ll;typedef pair<ll, ll> pll;//求解A[i]x = B[i] (mod M[i]),总共n个线性方程组 pll CRT(int n, ll A[], ll B[], ll M[]) { ll ans = 0, m = 1; for(int i = 0; i < n; i ++) { ll a = A[i] * m, b = B[i] - A[i]*ans, d = gcd(M[i], a); if(b % d != 0) return pll(0, -1);//答案不存在,返回-1 ll t = b/d * inv(a/d, M[i]/d)%(M[i]/d); ans = ans + m*t; m *= M[i]/d; } ans = (ans % m + m ) % m; return pll(ans, m);//返回的x就是答案,m是最后的lcm值 }
10.组合数取模
杨辉三角求解,复杂度O(
const int N = 1000 + 5;const int MOD = 1e9 + 7;int comb[N][N]; //comb[n][m]就是C(n,m)void init(){ for(int i = 0; i < N; i ++){ comb[i][0] = comb[i][i] = 1; for(int j = 1; j < i; j ++){ comb[i][j] = comb[i-1][j] + comb[i-1][j-1]; comb[i][j] %= MOD; } }}
逆元求解,复杂度O(n)
const int N = 200000 + 5;const int MOD = (int)1e9 + 7;int F[N], Finv[N], inv[N];//F是阶乘,Finv是逆元的阶乘 void init(){ inv[1] = 1; for(int i = 2; i < N; i ++){ inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD; } F[0] = Finv[0] = 1; for(int i = 1; i < N; i ++){ F[i] = F[i-1] * 1ll * i % MOD; Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD; }}int comb(int n, int m){//comb(n, m)就是C(n, m) if(m < 0 || m > n) return 0; return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;}
大组合数取模,卢卡斯定理,用于n和m特别大而p<1e5的情况
C(n, m) % p = C(n / p, m / p) * C(n%p, m%p) % p
11.康托展开
//康托展开,把一个数字num展开成一个数组s,k是数组长度void cantor(int s[], LL num, int k){ int t; bool h[k];//0到k-1,表示是否出现过 memset(h, 0, sizeof(h)); for(int i = 0; i < k; i ++){ t = num / fac[k-i-1]; num = num % fac[k-i-1]; for(int j = 0, pos = 0; ; j ++, pos ++){ if(h[pos]) j --; if(j == t){ h[pos] = true; s[i] = pos + 1; break; } } }}
以及,康托逆展开
//康托逆展开,把一个数组s换算成一个数字num void inv_cantor(int s[], LL &num, int k){ int cnt; num = 0; for(int i = 0; i < k; i ++){ cnt = 0; for(int j = i + 1; j < k; j ++){ if(s[i] > s[j]) cnt ++;//判断几个数小于它 } num += fac[k-i-1] * cnt; }}
12.容斥原理
容斥原理有很多实现方式,可以选择DFS,队列数组以及位操作的方式来实现。
以HDU-4135为例分别贴上代码
//队列数组#include<cstdio>using namespace std;typedef long long ll;int cnt,Cnt;int N[100],que[100000];void work(ll n){ //预处理n的所有质因子 Cnt = 0; for(int i=2;i*i<n;i++) if(n%i==0){ N[Cnt++] = i; while(n%i==0) n/=i; } if(n>1) N[Cnt++] = n; //这里是关键,在队列数组的顺位置决定了正负 cnt = 0; int l; for(int i=0;i<Cnt;i++){ l = cnt; for(int j=0;j<l;j++) que[cnt++] = que[j]*N[i]; que[cnt++] = N[i]; }}ll solve(ll a){ ll ans = a,flag = -1; for(int i=0;i<cnt;i++){ ans += flag* a/que[i]; flag = - flag; } return ans;}int main(){ int T; scanf("%d",&T); ll a,b,n; for(int tt=1;tt<=T;tt++){ scanf("%I64d %I64d %I64d",&a,&b,&n); work(n); printf("Case #%d: %I64d\n",tt,solve(b)-solve(a-1)); }}
//位操作#include<cstdio>using namespace std;typedef long long LL;int Cnt;LL N[100];void init(LL x){ Cnt = 0; for(int i=2;i*i<x;i++) if(x%i==0){ N[Cnt++] = i; while(x%i==0) x/=i; } if(x>1) N[Cnt++] = x;}LL work(LL x){ //接下来容斥 LL ans = x, cnt, temp; for(int i = 1; i < (1 << Cnt); i ++){ cnt = 0; temp = 1; for(int j = 0; j < Cnt; j ++){ if(i & (1 << j)){ temp *= N[j]; cnt ++; } } if(cnt & 1) ans -= x / temp; else ans += x / temp; } return ans;}int main(){ int T; LL l, r, n; scanf("%d", &T); for(int cas = 1; cas <= T; cas ++){ scanf("%I64d%I64d%I64d", &l, &r, &n); init(n); printf("Case #%d: %I64d\n", cas, work(r) - work(l-1)); }}
阅读全文
0 0
- 部分简单数论模板(未完待续)
- 算法笔记:数学问题+数论初步一(未完待续)
- 手机商务平台策划书(部分未完待续)
- 2015.11.29总结+部分题解(未完待续)
- POJ1753(未完待续)
- TreeMap(未完待续)
- HashTable(未完待续)
- string(未完待续)
- 方向(未完待续)
- 红黑树(未完待续)
- 源代码(未完待续)
- 绪论(未完待续)
- (未完待续)
- [Oracle]学习Oracle数据库的简单笔记(未完待续)
- LIST_ENTRY的介绍以及简单实用(未完待续)
- 德州扑克的部分逻辑 <未完待续>
- 简单的模板整理(待续)
- [未完待续][笔记]数论相关---基础
- String和StringBuilder的区别
- jdbc
- Java密码学
- 【Java多线程与并发库】02 传统定时器技术
- 2018.8.12网易校招内推笔试题
- 部分简单数论模板(未完待续)
- 单例模式
- Java 并发: Volatile 深入详解
- bootstrap引入cdn后无法修改CSS样式---CSS优先级机制
- Proxy(代理)服务器
- 金蝶K3 SQL报表系列-BOM成本明细表
- 拼多多2018校招内推编程题汇总
- oracle的guid用法
- Python笔记(Jinja2)