zoukankan      html  css  js  c++  java
  • 中等数论详解 [提高数论,积性函数)

    呼~好久没更了

    说得根有人看似的

    魔改自DZYdalao的ppt

    整除和剩余

    设a,b是两个整数,(b eq0),则存在唯一的q和r,使得

    a=qb+r,(0 leq r <|b|)

    即q=a/b,r=a%b

    这种式子叫带余除法

    若r=0,则称b整除a,记为b|a

    若a和b除以m的余数相同,称为a和b模m同余,记为

    (a equiv b (mod m))

    在模 m 的意义下,余数相同的归为一个集合,那么所有整数被分为 m个不同的集合,模 m 的余数分别为 0,1,2,3,...,m − 1,这些集合被称为模 m 剩余类(同余类)。

    一个整数的集合,对 m 取模后,余数遍历了 0,1,2,3,...,m − 1,那么该整数集合是模 m 的完全剩余系。

    素数

    定义:素数(质数)是大于 1 的正整数,并且除了 1 和它本身不能被其他正整
    数整除。大于 1 的非素数的正整数称为合数。

    素数定理:

    (pi(x))表示小于x的素数一共有多少个

    那么有(pi(x)approxfrac{x}{ln x})

    这东西一般人可能证不出来

    而且好像没啥用

    唯一分解定理

    每一个正整数都可以唯一的表示成质数的乘积,其中质数因子从小到大出现。换句话说,任意正整数 n 可以写成:

    (n = p_{1}^{a_{1}} p_{2}^{a_{2}} ... p_{k}^{a_{k}})
    (= prod_{i=1}^{k} p_{i}^{a_{i}})

    void devide(int x)
    {
    	tot=0;
    	for (int i=1;i<=cnt;i++)
    		if (x%pl[i]==0)
    		{
    			a[++tot]=pl[i];
    			p[tot]=0;
    			while (x%pl[i]==0)
    			{
    				x/=pl[i];
    				p[tot]++;
    			}
    		}
    	if (x!=1)
    	{
    		a[++tot]=x;
    		p[tot]=1;	
    	}
    }
    

    可以做到(O(sqrt{N}))预处理,(O(frac{sqrt{N}}{log sqrt{N}}))回答

    当然如果询问很多,也可以用欧拉筛实现(O(N))预处理,(O(logN))回答(不知道欧拉筛的后面有)

    即在欧拉筛时顺便统计每个数的最小质因子,每次除以最小质因子,由于每个数最多有(O(logN))个质因子,所以是对数级别的

    bool np[MAXN];
    int pl[MAXN],mp[MAXN],cnt;
    void init()
    {
    	np[1]=1;
    	for (int i=2;i<=MAX;i++)
    	{
    		if (!np[i]) pl[++cnt]=i,mp[i]=i;
    		for (int j=1;j<=cnt&&i*pl[j]<=MAX;j++)
    		{
    			np[i*pl[j]]=1;
    			mp[i*pl[j]]=pl[j];
    			if (i%pl[j]==0) break;
    		}
    	}
    }
    int a[MAXN],p[MAXN],tot;
    void devide(int x)
    {
    	while (x!=1)
    	{
    		int t=mp[x];
    		a[++tot]=t,p[tot]=0;
    		while (x%t==0) x/=t,++p[tot];
    	}
    }
    

    例题:poj 3421

    题意:
    给定 X,让你构造序列 $a_{1} ,a_{2} ,...,a_{n} $,满足 (a_{i}< a_{i+1} ,a_{i} |a _{i+1} ,a_{n} = X)。求最大的 n,以及在最大 n 下的方案数。

    把 X 质因数分解,把质因子任意排列,那么这个前缀积对应着一个最优序列。

    第二问是组合数学,就不讲了

    主要是不想写公式

    素数筛法

    由反证法,可知如果n是合数,那么它一定有一个小于(sqrt{n})的因数

    可以得到以下判定法:

    bool isprime(int x)
    {
        if (x==1)
            return false;
        for (int i=2;i*i<=x;i++)
            if (x%i==0)
                return false;
        return true;
    }
    

    瞎推一波可以发现,如果x是素数,那么它要么是2,要么%6=1或5

    可以优化:

    bool isprime(int x)
    {
        if (x==1)
            return false;
        if (x==2)
            return true;
        if (x%6!=1&&x%6!=5)
            return false;
        for (int i=2;i*i<=x;i++)
            if (x%i==0)
                return false;
        return true;
    }
    

    复杂度O((sqrt{n})

    素数筛法

    如果我们想知道1~n 中所有素数,那将是O((nsqrt{n}))的

    其实可以一次性把所有素数筛出来

    我们可以枚举 1 到 n 所有数,如果枚举一个数的时候它没有被筛去,那么说明他是一个质数。

    由调和级数,复杂度(O(NlogN))

    埃拉托斯特尼筛法

    我们可以枚举 1 到 n 所有数,如果枚举一个数的时候它没有被筛去,那么说明他是一个质数。

    对于质数,我们把大于它们且是它们倍数的数筛去。

    复杂度O(NloglogN),基本够用了

    int np[MAXN],pl[MAXN],cnt;
    void init()
    {
        np[1]=1;
        for (int i=2;i<=n;i++)
            if (!np[i])
            {
                pl[++cnt]=i;
                for (int j=i*2;j<=n;j+=i)
                    np[i]=1;
            }
    }
    

    如果你觉得一堆log碍眼还有一种严格(O(N))的欧拉筛法

    前几种筛法浪费时间的主要原因就是同一个合数被筛了很多遍,而欧拉则保证每个合数只被他的最小质因子

    先贴代码:

    bool np[MAXN];
    int pl[MAXN],cnt;
    void init()
    {
    	np[1]=1;
    	for (int i=2;i<=MAX;i++)
    	{
    		if (!np[i]) pl[++cnt]=i;
    		for (int j=1;j<=cnt&&i*pl[j]<=MAX;j++)
    		{
    			np[i*pl[j]]=1;
    			if (i%pl[j]==0) break;
    		}
    	}
    }
    

    上述代码其他都好理解,主要是if (i%pl[j]==0) break;

    在上面描述中,注意(i*pl[j])是被(pl[j])筛的而不是(i)

    那么,如果我们发现(i)含有(pl[j]),那么(i)一定含有一个不超过(pl[j])的质数,那么(i)乘上后面的质数的最小质因子永远不可能是那个质数,就可以直接跳出

    这样就可以保证所有数只被筛一次

    例题:poj 2689

    题意:给定 L,R,保证$ L,R ≤ 10^{9} ,R−L ≤ 10^{5}$ ,问 [L,R] 中距离最近的两个质数的距离。

    根据上面那个“如果n是合数,那么它一定有一个小于(sqrt{n})的因数”

    也就是说(sqrt R)以内的数可以筛掉[L,R]

    然后瞎搞就可以了

    欧拉函数

    定义:(varphi(x))=1~n中与n互质的数

    性质:

    1.p是素数 ⇔ (varphi(p))=p-1(很好证明)

    2.设 p 是一个素数,a 是一个正整数,那么

    (varphi(p^{a}) = p^{a} - p^{a-1} = p^{a-1}(p-1))

    也好证

    3.如果 m,n 互质,那么 φ(mn) = φ(m)φ(n),这叫欧拉函数的积性。注意要互质。

    可以构造一个矩阵:

    然后就可以证了

    4.如果
    (n = prod_{i=1}^{k} p_{i}^{a_{i}}),
    (varphi(n) = n prod_{i=1}^{k} {(1-frac{1}{p_{i}})})

    可以通过意识流积性证明

    5.(sum_{d|n} varphi(d) = n)

    什么?证明?

    你找一个n,然后把(frac{1}{n},frac{2}{n},...,frac{n-1}{n})写出来,然后化成最简分数,再按分母归类,然后就会发现一件神奇的事

    欧拉定理

    如果 a,m 互质,那么:

    (a^{varphi(m)} equiv 1 (mod m))

    证明留作课后习题

    特殊情况:费马小定理

    当 p 是质数,(a eq p) 时,有 (a ^ {p-1} ≡ 1 (mod p))

    广义欧拉定理

    若 a,m 互质,则 (a^{n} ≡ a^{n \% φ(m)} (mod m)。)

    若 a,m 不互质,则:

    (a ^{n} ≡ a^{n \% φ(m)}(mod m)qquad (n < φ(m)))

    (a^{n} ≡ a^{n \% φ(m)+φ(m)}(mod m)qquad otherwise)

    (例题:poj3090)[http://poj.org/problem?id=3090]

    题意:
    给出范围为 (0,0) 到 (n,n) 的整点,你站在原点处,问有多少个整点可见。

    易知(x,y)=1

    然后是个很裸的莫比乌斯反演

    枚举横坐标 x,那么不被挡住的纵坐标一定与 x 互质。
    ans = 2$sum _{i=1} ^{n}{varphi(i)} $ + 3,3 对应 (1,0),(0,1),(0,0) 三点。

    线性同余方程

    欧几里得算法

    一个广为人知的定理:gcd(a,b) = gcd(b,a%b)

    然后可以递归了

    每次至少一个数减少一半,故时间复杂度为O(log(a+b))

    int gcd(int a,int b)
    {
        return b? gcd(b,a%b):a;
    }
    

    扩展欧几里得算法

    考虑如何解下面这个不定方程:

    (ax+by=gcd(a,b))

    看gcd不顺眼

    (gcd(a,b) = gcd(b,a \% b))

    左边写出来:

    (bx’+(a \% b)y’=gcd(b,a \% b))

    (ax+by=bx’+(a \% b)y’)

    %是什么?

    (ax+by=bx’+(a-b[frac{a}{b}])y’)

    整理一下:

    (ax+by=ay’+b(x’-[frac{a}{b}]y’))

    可得:

    (x=y’,y=x’-[frac{a}{b}]y’)

    然后又可以递归啦

    边界为b=0,即ax+0y=a,假装a=1,y=0

    迭代回去就可以得到原方程的一组特解

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

    至于通解的话乱搞就行了

    扩展欧几里得算法的扩展

    一般不定方程

    原来那个方程好鬼畜啊,有没有更通用的呢?

    即ax+by=c

    很(wo)容(bu)易(hui)证明,这个方程有解的充要条件是gcd(a,b)|c

    所以把x和y乘上(frac{c}{gcd(a,b)})就可以了

    什么?通解?

    把a和b同时除以gcd得到a',b'

    (x'=x+k imes b',y' = y - k imes a'(k in Z))就是通解

    同余方程

    (ax equiv b(mod c))

    可得ax+cy=b (注意x,y可以为负数)

    然后套exgcd就好了

    [例题:poj1061](http://poj.org/problem?id=1061)

    题意:
    有一个长为 L 的首尾相连的数轴,有两只青蛙 A,B,它们初始点分别为s,t,A 青蛙每步往右跳 p 步,B 青蛙每步往右跳 q 步,现在它们同时开始跳,求同时跳多少步后能跳到同一点上,若无解则输出”Impossible”.

    列出方程即可求解

    逆元

    定义:(a,m)=1,(ab equiv 1 (mod m)),则称 b 为 a 模 m 意义下的逆元。记为(a^{-1})

    显然,模意义下的除法可以用乘逆元来代替。

    求法:

    1.费马小定理

    当m为质数时,(a^{-1}=a^{m-2})

    2.扩展欧几里得

    即求(ax equiv 1 (mod m))

    3.欧拉定理

    由于要写欧拉函数,比较麻烦,这里就不介绍了

    筛法:

    1.首先有(1^{-1}=1)

    2.令

    (m=ki+r,0 leq r<i)

    放到mod m下

    (ki+r=0)

    同时乘以(i^{-1}r^{-1})

    (kr^{-1}=-i^{-1})

    (i^{-1}=-[m/i](m\%i)^{-1})

    然后瞎取一下模就可以了

    inv[1]=1;
    for (int i=2;i<p;i++)
    	inv[i]=(long long)(p-p/i)*inv[p%i]%p;
    

    筛阶乘逆元

    先递推求出阶乘

    然后随便哪个方法求出fact[n]的逆元 注意判断有没有0,否则要找最后一个非0的

    然后根据除法可用乘逆元代替再推回去

    fact[1]=1;
    for (int i=2;i<=n;i++)
    	fact[i]=fact[i-1]*i%MOD;		
    inv[n]=qpow(fact[n],MOD-2);
    for (int i=n-1;i>=1;i--)
    	inv[i]=inv[i+1]*(i+1)%MOD;
    

    常用于组合数学

    中国剩余定理

    中国剩余定理主要解决以下方程:

    (x equiv a_{1}qquad(mod m_{1}))

    (x equiv a_{2}qquad(mod m_{2}))

    ... ...

    (x equiv a_{n}qquad(mod m_{n}))

    其中(m_{1},m_{2},...,m_{n})两两互质

    解此方程的难点在于需要同时满足n个方程

    那我们考虑一下如何把限制减少

    由于模的可加性,所以对于第i个方程,x加上(m_{i})后依然满足

    我们不妨令x=(sum_{i=1}^{n}( k_{i}frac{M}{m_{i}})),其中(M=prod _{i=1}^{n} m_{i})

    这样第i个方程就只受(k_{i}frac{M}{m_{i}})的影响

    那我们就强制让它等于(a_{i}),即令(k_{i}=a_{i}(frac{M}{m_{i}})^{-1})

    然后加起来就可以了

    int ans=0,lcm=1;
    for (int i=1;i<=k;i++)
         scanf("%lld",&a[i]);
    for (int i=1;i<=k;i++)
         scanf("%lld",&b[i]),lcm*=b[i],a[i]=(a[i]%b[i]+b[i])%b[i];
    for (int i=1;i<=k;i++)
    {
         int temp=lcm/b[i];
         int x,y;
         exgcd(temp,b[i],x,y);
         x=(x+b[i])%b[i];
         ans=(ans+qmul(qmul(a[i],temp,lcm),x,lcm))%lcm;
    }
    printf("%lld",(ans+lcm)%lcm);
    

    扩展中国剩余定理

    如果(m_{k})不互质,该怎么办呢?

    我们换一种思路

    假设我们求出了前k-1个方程解为x,(M_{k}= prod _{i=1}^{k-1} m_{i})

    即通解为(x+tM_{k})(t为整数)

    对于第k个方程:

    (x+tM_{k} equiv a_{k} qquad (mod m_{k}))

    (tM_{k} equiv a_{k} - xqquad (mod m_{k}))

    然后又可以扩欧了。

    如果该方程无解,说明原方程无解

    int M=m[1],ans=r[1],x,y;
    for (int i=2;i<=n;i++)
    {
    	int a=M,b=m[i],c=(r[i]-ans%b+b)%b;
    	int gcd=exgcd(a,b,x,y),bg=b/gcd;
    	x=qmul(x,c/gcd,bg);
    	ans+=x*M;
    	M*=bg;
    	ans=(ans%M+M)%M;
    }
    

    貌似和中国剩余定理没任何关系

    Baby-Step-Giant-Step

    简称BSGS,中文名分块大步小步法,拔山盖世,北上广深等

    这是一种思想,主要用于处理这种方程

    (a^{x} equiv b (mod c))

    根据抽屉原理,它的周期最多为c,所以暴力枚举是O(c)的

    BSGS法:

    (B=sqrt{c} ,x=iB-y)

    (a^{x}=a^{iB-c})

    可得(a^{iB-y} equiv b qquad (mod c))

    然后(a^{iB} equiv a^{y}b qquad (mod c))

    然后枚举i,把(a^{iB})丢进Hash,值为iB

    再枚举y,检查(a^{y}b)是否出现,如果有说明解为iB-y

    如果没有出现说明无解

    复杂度O((sqrt{c}))

    按理说写Hash可以做到O(1)查询,但除非遇到用(sang)心良(bing)苦(kuang)的出题人,用map一般就能过了

    void init()
    {
    	len=ceil(sqrt(c));
    	int del=qpow(a,len,c),now=del;
    	m[now]=len;
    	for (int i=2;i<=len;i++)
    	{
    		now=(now*del)%c;
    		m[now]=i*len;
    	}
    }
    int BSGS(int b)
    {
    	for (int j=0,now=1;j<=len;j++,now=(now*a)%c)
    		if (m[(now*b)%c])
    			return m[(now*b)%c]-j;
    }
    

    最后用一道神题结束这篇文章:

    SDOI 2011 计算器

    数论大杂烩

    第一个直接快速幂

    第二个扩欧,然后瞎取模使其最小

    第三个BSGS,注意是求最小非负整数解,所以上面的写法是错的(调了好久QAQ)

    应该随时记录最小值

    还有如果用-1表示无解要注意不要把它%掉了

    这个学了之后就是莫比乌斯反演之类的,以后可能会写

  • 相关阅读:
    wpf数据验证实例及常用方法小结
    wpf自定义colorpicker
    DataGrid绑定Dictionary问题
    DataTemplate和ControlTemplate的关系
    Validation Rule和Binding Group
    WPF converter(包含传递复杂参数)
    【链接】Eclipse的Debug调试技巧
    安装apache服务出错,无法启动此程序,因为计算机中丢失VCRUNTIME140.dll
    apache解压版安装服务
    apache——(OS 10048)通常每个套接字地址(协议/网络地址/端口)只允许使用一次。 : AH00072: make_sock: could not bind to address [::]:443
  • 原文地址:https://www.cnblogs.com/lstoi/p/9571204.html
Copyright © 2011-2022 走看看