//特点:速度快、可以判断一个小于 2^63 的数是不是素数 constint S =8;//随机算法判定次数一般 8~10 就够了//计算 ret = (a * b) % c a,b,c < 2^63
ll mult_mod(ll a, ll b, ll c){
a %= c;
b %= c;
ll ret =0;
ll tmp = a;while(b){if(b &1){
ret += tmp;if(ret > c){
ret -= c;//直接取模慢很多}}
tmp <<=1;if(tmp > c){
tmp -= c;}
b>>=1;}return ret;}//计算 ret = (a^n) % mod 快速幂
ll pow_mod(ll a, ll n, ll mod){
ll ret =1;
ll temp = a % mod;while(n){if(n &1){
ret =mult_mod(ret,temp,mod);}
temp =mult_mod(temp,temp,mod);
n >>=1;}return ret;}//通过a^(n-1)=1(mod n)来判断 n 是不是素数//n-1 = x*2^t 中间使用二次判断//是合数返回true,不一定是合数返回falseboolcheck(ll a, ll n, ll x, ll t){
ll ret =pow_mod(a,x,n);
ll last = ret;for(int i=1; i<=t; i++){
ret =mult_mod(ret,ret,n);if(ret ==1&& last !=1&&last != n-1){returntrue;}
last = ret;}if(ret !=1){returntrue;}else{returnfalse;}}//Miller_Rabin算法 //是素数返回true(可能是伪素数) //不是素数返回false boolMiller_Rabin(ll n){if(n <2){returnfalse;//1、0和负数都不是素数 }if(n ==2){returntrue;//2是素数 }if((n &1)==0){returnfalse;//除了2的偶数都不是素数 }
ll x = n -1;
ll t =0;while((x &1)==0){
x >>=1;
t++;}srand(time(NULL));for(int i=0; i<S; i++){
ll a =rand()%(n-1)+1;if(check(a,n,x,t)){returnfalse;}}returntrue;}
扩展欧几里得算法求逆元
ll exgcd(ll a, ll b, ll &x, ll &y){//扩展欧几里得算法if(b ==0){
x =1, y =0;return a;}
ll ret =exgcd(b, a%b, y, x);
y -= a / b * x;return ret;}
ll getInv(int a,int mod){//求a在mod下的逆元,不存在逆元返回-1
ll x,y;
ll d =exgcd(a, mod, x, y);return d ==1?(x % mod + mod)% mod :-1;}
并查集 Disjoint Set
/* 并查集模板 Disjoint Set */int n,par[maxn],rnk[maxn];/* 父节点和层数初始化 */voidinit(){for(int i=0; i<=n; i++){
par[i]= i;
rnk[i]=0;}}/* 记忆化搜索 */intfind_root(int x){return par[x]== x ? x : par[x]=find_root(par[x]);}/* 联合两个点 */voidunion_vertices(int x,int y){
x =find_root(x);
y =find_root(y);if(x == y)return;//两点已经相连 /* 寻找层数小的点连,以便减少复杂度 */if(rnk[x]> rnk[y])
par[y]= x;elseif(rnk[y]> rnk[x])
par[x]= y;else{
par[x]= y;
rnk[y]++;}}