模板:数论(持续更新)

求一个数的约数个数和所有约数的和

在这里插入图片描述

欧拉函数:

int Euler(int n) {
    int m = (int)sqrt(n + 0.5);
    int ans = n;
    for (int i = 2; i <= m; ++i) {
        if (n % i == 0) {
            ans = ans / i *(i - 1);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) ans = ans / n *(n - 1);
    return ans;
}

拓展中国剩余定理:

// r[i] 表示余数  m[i]表示约数
int excrt(int r[], int m[], int n)
{
    int M = m[0], R = r[0], x, y, d;
    for (int i = 1; i < n; ++i)
    {
        exgcd(M, m[i], d, x, y);
        if ((r[i] - R) % d) return -1;
        x = (r[i] - R) / d * x % (m[i] / d);
        R += x * M;
        M = M / d * m[i];
        R %= M;
    }
    return R > 0 ? R : R + M;
}

拓展bsgs模板

// 如果时限很小的话可以手写hash加速
int EX_BSGS(int a,int b,int p)
{
    a %= p;b %= p;
    if (p==1) return 0;
    if (!a&&!b) return 1;
    if (b==1) return -1;
    if (!a)return -1;
    int d,step=0,k=1;
    while ((d=gcd(a,p))!=1)
    {
        if (b%d){return -1;}
        step++;p/=d;b/=d;k=(ll)k*a/d%p;
        if (b==k){return step;}
    }
    map<int,int> mp;
    int m=ceil(sqrt(p)),t=b;mp.clear();
    for (int i=0;i<=m;i++){mp[t]=i;t=(ll)t*a%p;}
    int A=qpow(a,m,p);
    t = (ll)k*A%p;
    for (int i=1;i<=m;i++)
    {
        int ans=t;t=(ll)t*A%p;
        if (mp.find(ans)!=mp.end())
        {
            ans=i*m-mp[ans]+step;
            return ans;
        }
    }
    return -1;
}

欧拉函数打表:

const int N = 5e6+3;
int pcnt,prime[800000],euler[N];
bool st[N];

void getEulers(int n)
{ //欧拉筛的扩展
    euler[1] = 1;
    pcnt = 0;
    for (int i=2; i <= n; i++ ){
        if ( !st[i] ){
            prime[pcnt++] = i;
            euler[i] = i-1;
        }
        for(int j=0; prime[j] <= n/i; j++ ){
            st[ prime[j]*i ] = 1;
            if ( i % prime[j] == 0 ) {
                euler[ i*prime[j] ] = euler[i] * prime[j];
                break;
            }
            euler[ i*prime[j] ] = euler[i] * ( prime[j]-1 );
        }
    }
}

莫比乌斯函数打表

const int N = 5e4+15;
int prime[700000],pcnt;
bool mark[N];
int mu[N];
void getMu(){
    memset(mark,0,sizeof(mark));
    mark[0] = mark[1] = 1;
    mu[1] = 1;
    pcnt = 0;
    for(int i = 2; i < N ; i ++){
        if(!mark[i]){
            prime[pcnt++] = i;
            mu[i] = -1;
        }
        for(int j = 0 ; i*prime[j] < N && j < pcnt ; j ++){
            mark[i*prime[j]] = 1;
            if(i%prime[j] == 0)  break;
            else mu[i*prime[j]] = -mu[i];
        }
    }
}

组合数


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e5+10;
typedef long long LL;
const LL MOD = 998244353;
LL fac[maxn],inv[maxn];
LL POW(LL x,LL y){
	LL res = 1LL;
	while(y){
		if(y&1LL) res = res*x%MOD;
		x = x*x%MOD;
		y>>=1LL;
	}
	return res;
}
void Init(){
	fac[0] = inv[0] = 1LL;
	for(int i=1;i<maxn;i++){
		fac[i] = fac[i-1]*i%MOD;
		inv[i] = POW(fac[i],MOD-2);
	}
}
LL C(LL n,LL m){
	return fac[n]*inv[n-m]%MOD*inv[m]%MOD;
}
int main(void){
	Init();
	for(LL i=1;i<=12;i++){
		for(LL j=1;j<=i;j++){
			printf("C(%lld,%lld) = %lld\n",i,j,C(i,j));
		}
	}
	return 0;
}

ps:数大的时候 int全为long long

素数筛:

const int N = 1e7+15;
int prime[700000],pcnt;
bool mark[N];

// 如果变量名都相同的话,就不用传参了
//void getPrimes(int prime[],int N,int &pcnt)
void getPrimes()
{
    memset(mark,0,sizeof(mark));
    mark[0] = mark[1] = 1;
    pcnt = 0;
    for(int i = 2; i < N ; i ++)
    {
        if(!mark[i])  
        	prime[pcnt++] = i;
        for(int j = 0 ; i*prime[j] < N && j < pcnt ; j ++)
        {
            mark[i*prime[j]] = 1;
            if(i%prime[j] == 0)
                break;
        }
    }
}

小区间 筛 大区间:

int prime2[N];
bool flag[N];
int p2cnt;
// pcnt 和 prime都是 getPrime产生的
void getPrimes2(int L,int R)
{
    if(L <= 1) L = 2;
    memset(flag, false, sizeof flag);
    for(int i=0; i< pcnt && prime[i]<R; i++){
        for(int j=(L+0ll+prime[i]-1)/prime[i]*prime[i]; j<=R; j+=prime[i]){
            if(j!=prime[i]) flag[j-L]=true;
        }
    }
    p2cnt=0;
    for(int i=0;i<=R-L;i++){
        if(!flag[i]) prime2[p2cnt++]=L+i;
    }
}

快速乘:

ll qmul(ll a, ll b, ll mod) {
    ll res = 0;
    while(b) {
        if(b & 1) res = (res + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return res;
}

快速幂:

// 带快速乘的快速幂
ll qpow(ll m,ll k,ll mod)
{
	ll res=1%mod,t=m%mod;
	while(k)
	{
		if(k&1)res=qmul(res,t,mod);
		t=qmul(t,t,mod);
		k>>=1;
	}
	return res;
}

// 大数 int换成long long
int qpow(int m, int k, int mod)
{
	int res=1%mod,t=m%mod;
	while(k)
	{
		if(k&1)res=res*t%mod;
		t=t*t%mod;
		k>>=1;
	}
	return res;
}

大素数判定

bool Millar(int p){
    if(p==1)return 0;
    int t=p-1,c=0;
    while(t%2==0)t/=2,c++;
    for(int i=0;i<5;i++){
        int rd=rand()%p+rand();              //随机一个底数
        int a=qpow(rd,t,p),last=a;
        for(int j=1;j<=c;j++){
            last=1LL*a*a%p;
            if(last==1&&a!=1&&a!=p-1)return 0;
            a=last;
        }
        if(a!=1)return 0;
    }
    return 1;
}

求模逆:

int inv(int x,int mod)
{
	return qpow(x,mod-2,mod)%mod;
}

逆元打表:

long long re[N],inv[N],fac[N];
void getInv(int n){
    re[0] = inv[1] = fac[0] = 1;
    for(int i = 1;i <= n;++i) fac[i] = fac[i-1] * i % mod;
    for(int i = 2;i <= n;++i) inv[i] = (mod-mod/i)*inv[mod%i] % mod;
    for(int i = 1;i <= n;++i) re[i] = re[i-1] * inv[i] % mod;
}

long long c(int a,int b){
    if(a < 0) return 0;
    return fac[a]*re[b]%mod*re[a-b]%mod;
}

求GCD/LCM:

// 朴素版
int gcd(int a,int b)
{
	return b==0 ? a : gcd(b,a%b);
}
// 加速版
int qgcd(int a, int b)
{
	if(a == 0) return b;
	if(b == 0) return a;
	if(!(a & 1) && !(b & 1)) // a % 2 == 0 && b % 2 == 0;
		return qgcd(a >> 1, b >> 1) << 1;
	else if(!(b & 1))
		return qgcd(a, b >> 1);
	else if(!(a & 1))
		return qgcd(a >> 1, b);
	else
		return qgcd(abs(a - b), min(a, b));
}

int lcm(int a,int b)
{
	return a/gcd(a,b)*b;	 // 先除再乘 防爆操作
}

拓展欧几里得:

void exgcd(int a,int b,int &d,int &x,int &y)
{
    if(!b) d=a, x=1, y=0;
    else{
        exgcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}

求反素数:

typedef long long LL;
int ps[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
int n; int sum = 0, minx;
//u:质因子序号, last:质因子指数, p:约数之积, s:约数个数
void dfs(int u, int last, int p, int s)
{
	if (s > sum || s == sum && p < minx){ //更新反素数
		sum = s;
		minx = p;
	}
	for (int i = 1; i <= last; i ++ ) {
		if ( (LL)p*ps[u] > n ) break;
		p *= ps[u];
		dfs( u+1, i, p, s*(i+1) );
	}
}
int main()
{
	cin >> n;
	dfs(0, 30, 1, 1);
	cout << minx << endl;
	return 0;
}

矩阵运算:

const int MAXN=15;
int sz = 2;

struct Mat{
    int dt[MAXN][MAXN];
    Mat(int tp=0){                   // 初始化 单位阵 tp = 1
        for(int i=1;i <= sz;i++){
            for(int j=1;j <= sz;j++){
                if(i==j) dt[i][j]=tp;
                else dt[i-1][j-1]=0;
            }
        }
    }
    void read(int n,int m){
        for(int i = 1 ; i <= n ; i ++)
            for(int j = 1; j <= m ; j ++)
            {
                cin>>dt[i][j];
                scanf("%lld",&dt[i-1][j-1]);
            }
    }
    Mat operator+(const Mat& a){
        Mat res;
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                res.dt[i-1][j-1]=(dt[i-1][j-1]+a.dt[i-1][j-1])%MOD;
            }
        }
        return res;
    }
    Mat operator-(const Mat& a){
        Mat res;
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                res.dt[i-1][j-1]=(dt[i-1][j-1]-a.dt[i-1][j-1])%MOD;
            }
        }
        return res;
    }
    Mat operator*(const Mat& a){
        Mat res;
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                int sum=0;
                for(int k=1;k<=sz;k++){
                    sum=(sum+dt[i-1][k-1]*a.dt[k-1][j-1])%MOD;
                }
                res.dt[i-1][j-1]=sum;
            }
        }
        return res;
    }
};

// 矩阵快速幂
Mat pow_mat(Mat ans, Mat x, ll n) {

    while (n) {
        if (n&1)
            ans = x * ans;
        x = x * x;
        n >>= 1;
    }
    return ans;
}

整除分块:

int d = 0;
int res = 0;
for(int i = 1 ; i <= n ; i = d+1 ){
    d = n/(n/i);
    res += (d-i+1)*(n/i);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值