数论——板子

数论

欧几里得算法证明 O( logn )

gcd

求A和B的最大公约数
假设X为最大公约数
间接条件:X | A X | B

假设A >= B
建立方程式A + KB = C = A % B
∵ X | A X | KB
∴ X | A + KB
∴ X | C
∴ GCD(A,B)== GCD(B, A%B)
然后我们就可以不断的往下辗转相除

求GCD(B,C)
B和C最大公约数也是X
何时是个头
GCD(KX , X )其实这里能被整除,就已经知道求到GCD了
再往下
GCD (X , 0)

扩展欧几里得 O( logn )

exgcd

前提条件:d==gcd(a,b)
问题:ax+by=d,求x和y的通解

那么我们先建立一个方程组
A:ax1 + by1= d ==gcd(a,b)
B:bx2 + a%by2= d ==gcd(b,a%b)

B方程式展开:bx2 + ( a - a / b * b ) * y2=d
括号打开再合并:ay2 + b * ( x2 - a / b * y2 ) = d
所以:
x1=y2
y1=x2 - a / b * y2
不断递归下去到头

ax + 0*y = gcd(a,0);
所以x=1,y=0;

ax+by=c
如果 d | c 一定有解,否则一定无解

注意1:
exgcd求得是c == d的x,y
x= x * c/d, y = y * c/d;
这里才是正式的ax+by=c的x与y的一组解。

注意2:
exgcd求到得x,可能为负数,所以不断得加最小系数k1和减去k2
a * ( x + k1 )+ b * ( y - k2 )=d
展开后,保证a * k1= =b * k2 = = lcm(a,b)
所以 k1= lcm(a,b) / a , k2 = lcm(a,b)/b;
然后加成正数就行,但是不是最小正数,x>=k1,就得不断减去k1;

第一步:先求exgcd的,x,y
第二步:看是否需要加系数k1,k2

int exgcd(int a,int b,int &x1,int &y1)///求到c==gcd的一个x与y的解
{
    if(b==0)
    {
        x1=1,y1=0;
        return a;
    }
    int x2,y2;
    int gcd=exgcd(b,a%b,x2,y2);
    x1=y2,y1=x2-a/b*y2;
    return gcd;
}
x=(x%k1+k1)%k1;///这一步才是最小的正整数x,x>=k1,要降,x<0,要增加。

剩余定理

{ x = a 1 ( m o d   b 1 ) x = a 2 ( m o d   b 2 ) x = a 3 ( m o d   b 3 ) \left\{ \begin{array}{c} x=a1(mod\ b1) \\ x=a2(mod\ b2) \\ x=a3(mod\ b3) \end{array} \right. x=a1(mod b1)x=a2(mod b2)x=a3(mod b3)

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

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    int gcd=exgcd(b,a%b,x,y);
    int tp=x;
    x=y;
    y=tp-a/b*y;
    return gcd;
}

int CRT(vector<int> &A,vector<int> &B,int n)
{
    int x,y,k;
    int mod=B[0],ans=A[0];//第一个方程的解特判
    for(int i=1; i<n; i++)
    {
        int a=mod,b=B[i],c=(A[i]-ans%b+b)%b;//ax≡c(mod b)
        int gcd=exgcd(a,b,x,y),bg=b/gcd;
        if(c%gcd!=0)
            return -1; //判断是否无解,然而这题其实不用
        x=mul(x,c/gcd,bg);
        ans+=x*mod;//更新前k个方程组的答案
        mod*=bg;//M为前k个m的lcm
        ans=(ans%mod+mod)%mod;
    }
    return (ans%mod+mod)%mod;
}

多项式

在数学中,由若干个单项式相加组成的代数式叫做多项式(若有减法:减一个数等于加上它的相反数)。多项式中的每个单项式叫做多项式的项,这些单项式中的最高项次数,就是这个多项式的次数。其中多项式中不含字母的项叫做常数项。

本原多项式

在这里插入图片描述
高斯引理:本原多项式的乘积还是本原多项式。
(证明就算了吧,我不配)

组合数

N=p1^e1 * p2^e2 * p3^e3
N 的因子个数=(1+e1)* ( 1+e2) * (1+e3);
n个不同物品的排列数A(n,n)=A!(原理:加法原理+乘法原理)
公式:A(n,n)=n*(n-1)(n-2)···(n-n+1)=A!
变形一下:
在这里插入图片描述

从n个不同物品取m个的排列数:A(n,m)
公式:A(n,m)=n*(n-1)*(n-2)···(n-m+1);

从n个不同物品取m个的组合数:C(n,m)
公式:C(n,m)=A(n,m)/A(m,m)=n!/(n-m)!/m!;
这里提一下A(n,m)=P(n,m),只不过P是老版教材的用法
在这里插入图片描述
C n m = n ! m ! ∗ ( n − m ) ! C^m_n=\frac{n!}{m!*(n-m)!} Cnm=m!(nm)!n!
递推公式:
C n m = C n − 1 m + C n − 1 m − 1 C^m_n=C^m_{n-1}+C^{m-1}_{n-1} Cnm=Cn1m+Cn1m1
性质1:
C n m = C n n − m C^m_n=C^{n-m}_n Cnm=Cnnm
性质2:
C n + m + 1 m = ∑ i = 0 m C n + i i C^m_{n+m+1}=\sum^m_{i=0}{C^i_{n+i}} Cn+m+1m=i=0mCn+ii
性质3:
C n m ∗ C m r = C n r ∗ C n − r m − r C^m_n *C^r_m=C^{r}_n *C^{m-r}_{n-r} CnmCmr=CnrCnrmr
性质4:(二项式定理)
∑ i = 0 n C n i = 2 n \sum^n_{i=0}{C^i_n}=2^n i=0nCni=2n
性质5:(n为奇数,产生偶数个)
C n 0 − C n 1 + C n 2 − C n 3 … … C n n = 0 C^0_n-C^1_n+C^2_n-C^3_n……C^n_n=0 Cn0Cn1+Cn2Cn3Cnn=0
性质6:(根据性质4和性质5,并且m为奇数)
C n 0 + C n 2 + C n 4 … … = C n 1 + C n 3 + C n 5 … … = 2 n − 1 C^0_n+C^2_n+C^4_n……=C^1_n+C^3_n+C^5_n……=2^{n-1} Cn0+Cn2+Cn4=Cn1+Cn3+Cn5=2n1
性质7:
C n + m r = C n 0 ∗ C m r + C n 1 ∗ C m r − 1 … … + C n r ∗ C m 0 C^r_{n+m}=C^0_n*C^r_m+C^1_n*C^{r-1}_m……+C^r_n*C^0_m Cn+mr=Cn0Cmr+Cn1Cmr1+CnrCm0
性质8:
m ∗ C n m = n ∗ C n − 1 m − 1 m*C^m_n=n*C^{m-1}_{n-1} mCnm=nCn1m1
性质9:
∑ i = 1 n C n i ∗ i = n ∗ 2 n − 1 \sum^n_{i=1}{C^i_n*i}=n*2^{n-1} i=1nCnii=n2n1
性质10:
∑ i = 1 n C n i ∗ i 2 = n ∗ ( n + 1 ) ∗ 2 n − 2 \sum^n_{i=1}{C^i_n*i^2}=n*(n+1)*2^{n-2} i=1nCnii2=n(n+1)2n2
性质11:
∑ i = 0 n ( C n i ) 2 = C 2 n n \sum^n_{i=0}{(C^i_n)^2}=C^n_{2n} i=0n(Cni)2=C2nn
卢卡斯定理
C n m % p = C n / p m / p ∗ C n % p m % p % p , 无 脑 递 归 就 行 C^m_n \%p=C^{m/p}_{n/p}*C^{m\%p}_{n\%p} \%p,无脑递归就行 Cnm%p=Cn/pm/pCn%pm%p%p

杨辉三角——数据n<=1e3,时间空间复杂度O(1e6)

#define int long long
const int Clen=2e3+5;
const int mod=998244353;
int C[Clen][Clen];//C[总范围][小范围]
void getC()//数据要预处理
{
    for(int i=0;i<Clen;i++)
    {
        for(int j=0;j<=i;j++)
        {
            if(j==0||j==i)
                C[i][j]=1;
            else
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
}

LUCAS定理(留一手,大杀器)

Lucas定理是用于处理特大组合数取模的定理
通常用于解决阶乘无法解决的问题。
在这里插入图片描述

int Lucas(int n, int m)
{
   if(m==0)
       return 1;
   return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}

费马小定理——n<=1e6

(乘法逆元+快速幂+阶乘预处理)
预处理O(nlogn)
C(n,m)-O(1)

int fac[N],fac_inv[N];
int fpow(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
int C(int n,int m)
{
    if(n<m|m<0)
        return 0;
    return fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;
}
void init()
{
    fac[0]=1;
    for(int i=1;i<N;i++)
	    fac[i]=fac[i-1]*i%mod; 
    for(int i=0;i<N;i++)
	    fac_inv[i]=fpow(fac[i],mod-2);
}

插空法(处理不相邻问题)

插空法就是对于解决 某几个元素要求不相邻 的问题时,先将其他元素排好,再将所指定的不相邻的元素插入它们的间隙或两端位置。首要特点就是不相邻。

例题1:
把1,2,3,4,5组成没有重复数字且数字 1,2不相邻的五位数,则所有不同排法有多少种?
解析:本题直接解答较为麻烦,因为可先将 3,4,5三个元素排定,共有A(3,3)种排法,然后再将 1,2插入四个空位共有A(4,2)种排法,故由乘法原理得,所有不同的五位数有A(3,3)*A(4,2)=72种。

捆绑法(处理相邻问题)

所谓捆绑法,指在解决对于某几个元素要求相邻问题时,先整体考虑,将相邻元素视作一个整体参与排序,然后再单独考虑这个整体内部各元素间顺序。注意:其首要特点是相邻,其次捆绑法一般都应用在不同物体的排序问题中。
例题3:
6个不同的球放到5个不同的盒子中,要求每个盒子至少放一个球,一共有多少种方法?
解答:根据题目要求,则其中一个盒子必须得放 2 个,其他每个盒子放 1 个球,所以从 6 个球中挑出 2 个球看成一个整体,则有C(6,2),这个整体和剩下 4 个球放入 5 个盒子里,则有A(5,5)。方案数:C(6,2)*A(5,5)=1800。

隔板法(处理相同元素+不同分组每组至少存在Si个)

基本题型
基本题型为:n个相同元素,不同个m组,每组至少有一个元素;则只需在 n 个元素的n-1 个间隙中放置 m-1 块隔板把它隔成 m 份,求共有多少种不同方法?

其解题思路为:将 n 个相同的元素排成一行, n 个元素之间出现了( n-1 )个空档,现在我们用( m-1 )个 “档板 ”插入( n-1 )个空档中,就把 n 个元素隔成有序的 m 份,每个组依次按组序号分到对应位置的几个元素(可能是 1 个、2 个、 3 个、 4 个、 ….),这样不同的插入办法就对应着 n 个相同的元素分到 m 组的一种分法,这种借助于这样的虚拟 “档板 ”分配元素的方法称之为插板法。

例题4:
共有 10 完全相同的球分到 7 个班里,每个班至少要分到一个球,问有几种不同分法?
解析:我们可以将 10 个相同的球排成一行, 10 个球之间出现了 9 个空隙,现在我们用 6 个档板 ”插入这 9个空隙中,就 “把 10 个球隔成有序的 7 份,每个班级依次按班级序号分到对应位置的几个球(可能是 1 个、2 个、 3 个、 4 个),这样,借助于虚拟 “档板 ”就可以把 10 个球分到了 7 个班中。
基本题型的变形
(1)变形1:有 n 个相同的元素,要求分到 m 组中,问有多少种不同的分法?
解题思路:这种问题是允许有些组中分到的元素为 “0”,也就是组中可以为空的。对于这样的题,我们就首先将每组都填上 1 个,这样所要元素总数就 m 个,问题也就是转变成将( n+m )个元素分到 m 组,并且每组至少分到一个的问题,也就可以用插板法来解决。

例题:有 8 个相同的球放到三个不同的盒子里,共有C(7,2)种不同方法 。

解答:题目允许盒子有空,则需要每个组添加 1 个,则球的总数为 8+3 ×1=11,此题就有 C(10 ,2) =45(种)分法了。

(2)变形2:有 n 个相同的元素,要求分到 m 组,要求各组中分到的元素至少某个确定值 S( s>1,且每组的 s值可以不同) ,问有多少种不同的分法?

解题思路: 这种问题是要求组中分到的元素不能少某个确定值 s,各组分到的不是至少为一个了。 对于这样的题,我们就首先将各组都填满,即各组就填上对应的确定值 s 那么多个,这样就满足了题目中要求的最起码的条件,之后我们再分剩下的球。这样这个问题就转变为上面提到的变形1的问题了,也就可以用插板法来解决。

排列组合问题编辑
排列组合问题从解法看,大致有以下几种:
(1)有附加条件的排列组合问题,大多需要分类讨论的方法,注意分类时应不重不漏;
(2)排列与组合的混合型问题,用分类加法或分步乘法计数原理解决;
(3)元素相邻,可以看作是一个整体的方法;
(4)元素不相邻,可以利用插空法;
(5)间接法,把不符合条件的排列与组合剔除掉;
(6)穷举法,把不符合条件的所有排列或组合一一写出来

埃式筛

埃式筛----O(nlog log n)n=2e7 loglon(n)=2.8,也就二倍多一点,大胆去冲.,T了再优化,后悔就来不及了
核心:枚举每个数所有因子

const int N=6.5e4+5;
int a[N];
bool prime[N];///判断该位置是否为素数(素数==ture 非素数=false)
void get_prime()
{
    for(int i=2; i<N; i++) ///埃式筛原理
    {
        if(a[i])
            continue;
        prime[i]=true;
        for(int j=i; j<N; j+=i)
            a[j]=true;
    }
}

欧拉筛----O(n)

核心:
确定为合数:根据最小质因子确定的(仅仅确定一次)——O(2n)
第一个n遍历,第二个n是确定合数
避免确定合数两次,第二次i%it==0得break;
举例:8以内质数存在2 3 5,我们只需要确定2
8为合数,3*8不用,因为不是由最小合数确定的。后面,就更不用举例了,最小合数都是2了,

vector<int>prime;
bool vis[N];//该位置是否为素数
void get_prime()
{
    for(int i=2;i<N;i++)
    {
        if(vis[i]==0)
            prime.pb(i);
        for(auto it:prime)
        {
            if(it*i>=N)
                break;
            vis[it*i]=true;
            if(i%it==0)
                break;
        }
    }
}

大素数应用(针对1e18以内)

MillerRabin素数测试+Pollard Rho大数质因数分解算法

学习链接
期望时间复杂度 O(n1/4),一般比这个还要快
srand(time(0)),记得设置种子

说明:
1:MillerRabin判断一个数是否为素数
2:Pollard Rho得到随机数的因子
code解释:

bool MR(x)		MillerRabin算法,素数判断
int find(x)		Pollard-Rho算法,获得随机因子
int PR(x)		PR递归因子分解,递归随机因子,每个PR()就是一个因子
struct PM
{
    PM()
    {
        srand(time(0));
    }
    int MUL(int a,int b,int p) //a*b%p
    {
        int x=(long double)a/p*b;
        return ((ull)a*b-(ull)x*p+p)%p;
    }
    int POW(int a,int b,int p) //a^b%p
    {
        if(!b)
            return 1;
        if(b==1)
            return a;
        int sum=POW(a,b/2,p);
        if(b%2)
            return MUL(MUL(sum,sum,p),a,p);
        return MUL(sum,sum,p);
    }
    int f(int x,int c,int p)
    {
        return (MUL(x,x,p)+c)%p;
    }
    ///MillerRabin算法,素数判断
    bool MR(int x)
    {
        if(x==0||x==1)
            return false;
        if(x==2)
            return true;
        if(x%2==0)
            return false;
        int p=x-1,q=0;
        while(p%2==0)
            q++,p/=2;
        for(int i=1; i<=10; i++)
        {
            int a=rand()%(x-1)+1;
            if(POW(a,x-1,x)!=1)
                return false;
            int lst=1;
            for(int j=0; j<q; j++)
            {
                int t=POW(a,(1ll<<j)*p,x);
                if(t==1&&lst!=1&&lst!=x-1)
                    return false;
                lst=t;
            }
            if(lst!=1&&lst!=x-1)
                return false;
        }
        return true;
    }
    ///Pollard-Rho算法,获得随机因子
    int find(int x)
    {
        if(x%2==0)
            return 2;
        if(MR(x))
            return x;
        int t=rand()%(x+1);
        int a=t,b=t;
        int c=rand()%(x+1);
        while(1)
        {
            a=f(a,c,x),b=f(f(b,c,x),c,x);
            int d=__gcd(abs(a-b),x);
            if(d>1&&d<x)
                return d;
            if(a==b)
                return find(x);
        }
    }
    ///PR递归因子分解,递归随机因子,每个PR()就是一个因子
    int PR(int x)
    {
        if(x<=1)
            return 1;
        if(MR(x))
            return x;
        int y=find(x);
        return max(PR(y),PR(x/y));
    }
};

数论分块

∑ i = 1 n n / i \sum_{i=1}^n n/i i=1nn/i
一般问题:n=1e12,O( 2 n 2\sqrt n 2n
方便理解code

    long long sum=0,n;
    cin>>n;
    for(int l=1,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        sum+=(r-l+1)*(n/l);
    }
    cout<<sum<<endl;

板子

int get(int n,int R)
{
    int sum=0;
    if(R<1)
        return 0;
    R=min(R,n);
    for(int l=1,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        r=min(r,R);
        sum+=(r-l+1)*(n/l);
        if(r==R)
            break;
    }
    return sum;
}

矩阵快速幂

在这里插入图片描述
斐波那契数列前n项和
f [ n ] = f [ n − 1 ] + f [ n − 2 ] f[n]=f[n-1]+f[n-2] f[n]=f[n1]+f[n2]
p r e [ n ] = p r e [ n − 1 ] + f [ n ] pre[n]=pre[n-1]+f[n] pre[n]=pre[n1]+f[n]
p r e [ n ] = f [ n + 2 ] − 1 pre[n]=f[n+2]-1 pre[n]=f[n+2]1

1:单位矩阵
2:初始矩阵

const int mod=998244353;
struct matrix
{
    int G[2][2];
};
matrix mul(matrix a,matrix b)
{
    matrix ans;
    memset(ans.G,0);
    for(int i=0; i<2; i++)
        for(int j=0; j<2; j++)
            for(int k=0; k<2; k++)
                ans.G[i][j]=(ans.G[i][j]+a.G[i][k]*b.G[k][j]%mod)%mod;
    return ans;
}
matrix in_pow(matrix a,int b)
{
    matrix ans;
    ans.G[0][0]=1,ans.G[0][1]=0;
    ans.G[1][0]=0,ans.G[1][1]=1;
    while(b)
    {
        if(b&1)
            ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}
int mypow(matrix a,int n,matrix c)
{
    return mul(in_pow(a,n),c).G[1][0];
}

博弈

nim模型

模型: n堆物品,每堆有 ai个,两个玩家轮流取走任意一堆的任意个物品,但不能不取。

取走最后一个物品的人获胜。
nim和=sg(a1)^ sg(a2) ^ ……^ sg(an)

sg函数

对于状态 x和它的所有 y1,y2,y3……个后继状态 ,定义SG 函数:
SG(x)=mex( SG(y1),SG(y2),……);
先确定所有终态

  1. 如果终态为lose,则mex所有后继状态
  2. 如果终态为win,则mex后继状态,避开所有的终态

高精度

大数整型+ - * % 板子随便用

string delete_leading0(string a)//处理前导0
{
    while(a.size()>1&&a[0]=='0')
        a.erase(a.begin());
    return a;
}
int cmp(string a,string b)//字符串比较大小
{
    a=delete_leading0(a);
    b=delete_leading0(b);
    int len_a=a.size(),len_b=b.size();
    if(len_a<len_b||(len_a==len_b&&a<b))
        return -1;
    if(len_a>len_b||(len_a==len_b&&a>b))
        return 1;
    return 0;
}
string add(string a,string b)//大数加法
{
    a=delete_leading0(a);
    b=delete_leading0(b);
    if(a.size()<b.size())
        swap(a,b);
    string ans;
    reverse(a.begin(),a.end());
    reverse(b.begin(),b.end());
    int len_a=a.size(),len_b=b.size(),sum=0;
    for(int i=0; i<len_a; i++)
    {
        if(i<len_b)
            sum+=a[i]+b[i]-'0'*2;
        else
            sum+=a[i]-'0';
        ans.push_back(sum%10+'0');
        sum/=10;
    }
    if(sum)
        ans.push_back(sum+'0');
    reverse(ans.begin(),ans.end());
    return ans;
}
string sub(string a,string b)//大数减法
{
    int flag=0;
    if(cmp(a,b)<0)
        swap(a,b),flag=1;
    for(int i=a.size()-1,j=b.size()-1; i>=0; i--,j--)
    {
        a[i]=a[i]-(j>=0?b[j]-'0':0);
        if(a[i]<'0')
        {
            a[i]+=10;
            a[i-1]--;
        }
    }
    while(a.size()>1&&a[0]=='0')
        a.erase(a.begin());
    if(flag)
    {
        reverse(a.begin(),a.end());
        a.push_back('-');
        reverse(a.begin(),a.end());
    }
    return a;
}
int bignum_mod(string a,int mod)//大数求余
{
    int sum=0,len=a.size();
    for(int i=0; i<len; i++)
        sum=(sum*10+a[i]-'0')%mod;
    return sum;
}
string div(string str,int b,int &k)
{
    string ans;
    str=delete_leading0(str);
    k=0;//保存余数
    for(auto it:str)
    {
        k=k*10+it-'0';
        ans+='0'+k/b;
        k%=b;
    }
    return delete_leading0(ans);
}
string mul(string a,string b)//大数乘法
{
    a=delete_leading0(a);
    b=delete_leading0(b);
    int la=a.size(),lb=b.size();
    vector<int> ans(la+lb+1);
    string s;
    for(int i=0; i<la; i++)
        for(int j=0; j<lb; j++)
            ans[i+j+1]+=(a[i]-'0')*(b[j]-'0');
    for(int i=la+lb-1; i>0; i--)
    {
        ans[i-1]+=ans[i]/10;
        ans[i]%=10;
    }
    for(int i=0; i<=la+lb-1; i++)
    {
        if(i==0&&ans[i]>0)
            s.push_back(ans[i]+'0');
        if(i)
            s.push_back(ans[i]+'0');
    }
    s=delete_leading0(s);
    return s;
}

备用运算符号重载

struct Big
{
    string str;
    Big(string _str="0")
    {
        str=_str;
    }
    void input()
    {
        cin>>str;
    }
    void output()
    {
        cout<<str<<endl;
    }
    bool operator==(const Big &b)const
    {
        return cmp(str,b.str)?false:true;
    }
    bool operator<(const Big &b)const
    {
        return cmp(str,b.str)<0?true:false;
    }
    bool operator>(const Big &b)const
    {
        return cmp(str,b.str)>0?true:false;
    }
    Big operator+(const Big &b)const
    {
        return add(str,b.str);
    }
    Big operator*(const Big &b)const
    {
        return mul(str,b.str);
    }
    Big operator-(const Big &b)const
    {
        return sub(str,b.str);
    }
};

欧拉函数

巨佬博客学习
单点筛[1,n]与n互质的个数
O ( n ) ϕ ( n ) O(\sqrt{n})\phi{(n)} On ϕ(n)

int get_phi(int  x){
    int res=x;
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0){
            res=res-res/i;
            while(x%i==0) x/=i;
        }
    }
    if(x!=1) res=res-res/x;
    return res;
}

埃式筛欧拉函数 O ( n l o g n ) O(nlogn) Onlogn

struct aishi_phi///nlogn
{
    vector<int>p;
    aishi_phi(int n)
    {
        p.resize(n);
        for(int i=1;i<n;i++)
            p[i]=i;
        for(int i=2;i<n;i++)
        {
            if(p[i]==i)
            {
                for(int j=i;j<n;j+=i)
                    p[j]=p[j]/i*(i-1);
            }
        }
    }
};

欧拉筛欧拉函数 O ( n ) O(n) On

struct euler_phi
{
    vector<int>p,prime,vis;
    euler_phi(int n)
    {
        p.resize(n+1);
        vis.resize(n+1);
        p[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!vis[i])
            {
                prime.pb(i);
                p[i]=i-1;
            }
            for(auto it:prime)
            {
                if(it*i>n)
                    break;
                vis[it*i]=true;
                if(i%it==0)
                {
                    p[i*it]=p[i]*it;
                    break;
                }
                else
                    p[i*it]=p[i]*p[it];
            }
        }
    }
};

莫比乌斯

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = p r i m e ] \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==prime] i=1nj=1m[gcd(i,j)==prime] max(n,m)<=1e7
init O ( n ) O(n) On
solve O ( n ) O(\sqrt{n}) O(n )
如果 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = 1 ] \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==1] i=1nj=1m[gcd(i,j)==1]

/*for (int i=1; i<=cnt; i++)
        for (int j=1; prime[i]*j<N; j++)
            f[j*prime[i]]+=mu[j];*/
    for (int i=1; i<N; i++)
        sum[i]=sum[i-1]+mu[i];
const int N=1e7+5;
int mu[N];
int flag[N],prime[N],cnt,f[N],sum[N];
void init()
{
    mu[1]=1;
    for (int i=2; i<N; i++)
    {
        if (!flag[i])
            prime[++cnt]=i,mu[i]=-1;
        for (int j=1; j<=cnt&&i*prime[j]<N; j++)
        {
            flag[i*prime[j]]=1;
            if (i%prime[j]==0)
                break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    for (int i=1; i<=cnt; i++)
        for (int j=1; prime[i]*j<N; j++)
            f[j*prime[i]]+=mu[j];
    for (int i=1; i<N; i++)
        sum[i]=sum[i-1]+f[i];
}
int solve(int a,int b)
{
    int ans=a*b;
    if (a>b)
        swap(a,b);
    for (int l=1,r=0; l<=a; l=r+1)
    {
        r=min(a/(a/l),b/(b/l));
        ans-=(sum[r]-sum[l-1])*(a/l)*(b/l);
    }
    return ans;
}

min_25筛

真正用法:求解积性函数 f ( x ) f(x) f(x)前缀和。
挖个坑,以后再补

时间复杂度:O( n 3 4 l o g n \frac{n^{\frac{3}{4}}}{logn} lognn43)
非线性筛1e10质数前缀和, p r i m e prime prime 数组存1e10数组所有质数。0.05s计算出来,记得每次使用初始化

typedef long long ll;
struct Min25 {

    static const int N=1e6+10;
    ll prime[N], id1[N], id2[N], flag[N], ncnt, m;

    ll g[N], sum[N], a[N], T, n;

    inline int ID(ll x) {
        return x <= T ? id1[x] : id2[n / x];
    }

    inline ll calc(ll x) {
        return x * (x + 1) / 2 - 1;
    }

    inline ll f(ll x) {
        return x;
    }
    inline void Init() {
        memset(prime, 0, sizeof(prime));
        memset(id1, 0, sizeof(id1));
        memset(id2, 0, sizeof(id2));
        memset(flag, 0, sizeof(flag));
        memset(g, 0, sizeof(g));
        memset(sum, 0, sizeof(sum));
        memset(a, 0, sizeof(a));
        ncnt = m = T = n = 0;
    }
    inline void init() {
        T = sqrt(n + 0.5);
        for (int i = 2; i <= T; i++) {
            if (!flag[i]) prime[++ncnt] = i, sum[ncnt] = sum[ncnt - 1] + i;
            for (int j = 1; j <= ncnt && i * prime[j] <= T; j++) {
                flag[i * prime[j]] = 1;
                if (i % prime[j] == 0) break;
            }
        }
        for (ll l = 1; l <= n; l = n / (n / l) + 1) {
            a[++m] = n / l;
            if (a[m] <= T) id1[a[m]] = m; else id2[n / a[m]] = m;
            g[m] = calc(a[m]);
        }
        for (int i = 1; i <= ncnt; i++)
            for (int j = 1; j <= m && (ll)prime[i] * prime[i] <= a[j]; j++)
                g[j] = g[j] - (ll)prime[i] * (g[ID(a[j] / prime[i])] - sum[i - 1]);
    }

    inline ll solve(ll x) {
        if (x <= 1) return x;
        return n = x, init(), g[ID(n)];
    }

}a;

高斯消元(处理线性方程组)

const int N=1e2+5;
double a[N][N],ans[N];
signed main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n+1;++j)
            cin>>a[i][j];
    for(int i=1;i<=n;++i) //枚举列(项)
    {
        int maxn=i;
        for(int j=i+1;j<=n;++j) //选出该列最大系数
            if(fabs(a[j][i])>fabs(a[maxn][i]))
                maxn=j;
        for(int j=1;j<=n+1;++j) //交换
            swap(a[i][j],a[maxn][j]);
        if(!a[i][i])//最大值等于0则说明该列都为0,肯定无解
        {
            puts("No Solution");
            return 0;
        }
        for(int j=1;j<=n;++j) //每一项都减去一个数(即加减消元)
        {
            if(i==j)
                continue;
            double temp=a[j][i]/a[i][i];
            for(int k=i+1; k<=n+1; ++k)
                a[j][k]-=a[i][k]*temp;
        }
    }
    decimal(2);
    for(int i=1;i<=n;i++)
    {
        ans[i]=a[i][n+1]/a[i][i];
        cout<<ans[i]<<" \n"[i==n];
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值