Noip前的大抱佛脚----数论

Posted xzyxzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Noip前的大抱佛脚----数论相关的知识,希望对你有一定的参考价值。

数论

知识点

Exgcd

(O(logn))求解(Ax+By=C)的问题
1、若(C\\%gcd(A,B)!=0)则无解
2、(Gcd=gcd(A,B);A/=Gcd,B/=Gcd,C/=Gcd)
3、代入下面代码求(Ax+By=1)
4、(x*C),得到一组特解
5、通解为(egin{cases}x=x_0+k*B \\y=y_0+k*Aend{cases})

void Exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b){x=1;y=0;return;}
    Exgcd(b,a%b,y,x);y-=a/b*x;
}

逆元

(gcd(A,P)==1)时,(A)在模(P)意义下存在逆元(证明可以符合Exgcd有解的证明),其余情况不存在逆元

通常(P)为质数时就是(A^{p-2})作为逆元(费马小定理)

  • 欧拉定理(费马小定理) (x^{-1}equiv x^{p-2}(mod p)),要求(p)为质数

  • 解方程(Exgcd)(Px+Ay=1,A=frac{1}{y}(mod P))

  • 线性递推逆元:

    (a=lfloorfrac{p}{i} floor,b=p\\%i),则有(ai+b=p,iequiv-frac{b}{a}(mod p),frac{1}{i}equiv-frac{a}{b}(mod p))

    由于(b<i),所以(b)的逆元已经求出,直接可以得到:

    inv[i]=p-(p/i*inv[p%i])%p;

    ?

gcd

具有一些奇妙的性质,如可合并

  • 往数集中加入一个数,要么gcd不变,要么至少变为原来的(frac{1}{2})(所以可以用来分块了(同样的xor也每次只会增加log次))

欧拉函数(varphi(x))

表示小于x且与x互质的数的个数

计算公式

[varphi(n)=n*prod(1-frac{1}{p_i})],其中(p_i)表示(n)的不相同的质因子

欧拉公式

[a^{varphi(p)}equiv 1 (mod p)]

降幂公式

  • (x,p)互质:(x^kequiv x^{k\\%varphi(p)} (mod p))
  • (x,p)不互质:(x^kequiv egin{cases}x^{k} (mod p),klevarphi(p) \\x^{k\\%varphi(p)+varphi(p)} (mod p),k>varphi(p)end{cases})

CRT&EXCRT

(O(nlogn))求解一系列同余方程,如

[egin{cases}xequiv A_1(mod P_1) \\xequiv A_2(mod P_2) \\...\\xequiv A_n(mod P_n) \\end{cases}]

代码如下(未判无解)

ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
void Exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b) {x=1;y=0;return;}
    Exgcd(b,a%b,y,x);y-=a/b*x;
}
int EXCRT()
{
    for(int i=2;i<=n;i++)
    {
        ll g=gcd(P[i-1],P[i]),C=(A[i]-A[i-1])/g,x,y;
        Exgcd(P[i-1]/g,P[i]/g,x,y);
        P[i]=P[i-1]*P[i]/g;
        A[i]=A[i-1]+P[i-1]*x*C;
        A[i]=(A[i]%P[i]+P[i])%P[i];
    }
    return A[n];
}

方法是每次合并两个方程,手推式子(P[i]x+A[i]=P[i+1]y+A[i+1] -> P[i]x+P[i+1]y=A[i+1]-A[i])

可以发现模数为原来的(lcm)(先除以(gcd)再乘,很容易爆(long long)),余数为原来余数加上模数的(x)

注意经常x算出来是负数所以时刻(+mod)\\%mod !)

有的时候题目并没有那么裸

  • (x)前带系数(屠龙勇士)

(Axequiv B(mod p) -> Ax+pk=B(当且仅当B\\%gcd(A,p)==0时有解) -> x=x_0+tp -> xequiv x_0(mod p))

  • CRT合并答案

对于一些公式,只适用于模数是质数的情况,而题目要求的模数要是任意正数,所以可以求得

[egin{cases}Ansequiv A_1(mod P_1) \\Ansequiv A_2(mod P_2) \\...\\Ansequiv A_n(mod P_n) \\end{cases}]

然后用CRT合并答案,求得(Ansequiv OUTANS (mod Mod))

但是就所见到的题目来说(任意模数NTT),只有在答案不太大的时候适用,例如该题答案不会超过(10^9*10^9*10^5=10^{23}),方法是选取三个乘积大于(10^{23})的NTT模数,得到

[egin{cases}Ansequiv A_1(mod P_1×P_2) \\Ansequiv A_2(mod P_3) \\end{cases}]

(M=P_1*P_2)于是(Ans=kM+A_1=k_3P_3+A_2),即(kM+A_1equiv A_2(mod P_3))

由于(Ansle10^{23}<P_1P_2P_3=MP_3),所以(k<P_3),根据此同余方程可以求得(k)(mod P_3)的解也就是(k)的实际值

然后就可以带入求得答案(Ans=kM+A_1)了,不过会爆(long long),可以采用这个

ll mul(ll x,ll y,ll p) {return (x*y-(ll)(long double)x/p*y+0.5)*p+p)%p;}

BSGS&EXBSGS

快速((O(sqrt n)))求解(A^x=B(mod P))(x)的解,板子题:[SDOI2013]随机数生成器

普通的BSGS要求P是质数,拓展的可以不用是质数

通过降幂公式,能够知道(xle P-1),令(M=sqrt P),则(x=iM+j),可以枚举(i)的值,得到(t=A^{(i+1)M}),查表看是否存在(T=A^{M-j}B)满足条件,得到解就返回,这样就能保证解是最小的了

int BSGS(int A,int B,int P)
{
    if(!A%P) return -1;
    int M=sqrt(P)+1;Hash.reset();
    for(int i=0,t=B;i<M;i++,t=1ll*t*A%P) Hash.Add(t%Mo,t,i);//存入B*A^i的哈希值
    for(int i=1,bs=ksm(A,M,P),t=bs;i<=M;i++,t=1ll*t*bs%P)//t=A^{(i+1)M}
        if(Hash.Query(t)!=-1) return i*M-Hash.Query(t);
    return -1;
}

FFT/NTT/MTT/FWT

板子

原根相关

定义:

原根类似于FFT的单位复根,使得能够进行取模操作

NTT模数是指有原根且是(r×2^k+1)的形式的模数,如(998244353(3))(1004535809(3))

存在性及判定

一个数有原根当且仅当它为(2,4,p,2p,p^r)(p为奇质数)

(g^1,g^2...g^{phi(p)})(mod p)下各不相同,且(g^{phi (p)}=1),当p为质数时,也就是说(g^1...g^{p-1})分别对应(1...p-1)

(p)为质数时,令(p-1=p_1^{a1}p_2^{a_2}...p_n^{a_n}),若存在(g^{frac{p-1}{p_i}}=1)(g)不是原根,否则是原根(证明

注意点

  • 两个多项式最高次项分别为(l1,l2),那么需要开的长度(包括0)是(>l1+l2)的第一个形如(2^k)的数

组合公式

  • ((0,0))走到((n,m)),不碰到直线(y=x+b)的方案数:(C(n+m,n)-C(n+m,n-b))

    意义为沿着直线翻折,越过直线的路径对称域从翻着顶点到终点的路径

斯特林数

第一类斯特林数

第一类斯特林数:(n)个人坐(m)张圆桌的方案数(人不同,圆桌相同,n个元素构成m个圆排列)

[s[n,m]=s[n-1,m-1]+s[n-1,m]*(n-1)]

第二类斯特林数

第二类斯特林数:(n)个球放入(m)个盒子的方案数(球不同,盒子相同,n个元素分成m个非空集合)

[S{n,m}=S{n-1,m-1}+S{n-1,m}*m]

常用数学公式

  • 平方和的前缀和 (sum_{i=1}^{n}i^2=frac{n(n+1)(2n+1)}{6})
  • 立方和的前缀和 (sum_{i=1}^{n}i^3=(frac{n(n+1)}{2})^2)

位运算

子集枚举

(0)~(2^n)的子集个数之和是(3^n)(一个位置可以有三种情况:没被选到、选到但是没有被子集枚举到、选到并被枚举到)

以下可以快速求子集

for(int i=s;;i=(i-1)&s)
{
    //do whatever you want
    if(!i) break;
}

高维前缀和

统计子集

for(int p=0;p<=20;p++)
    for(int i=0;i<1<<p;i++)
        if(i&(1<<p)) f[i]+=f[i^(1<<p)];

统计超集

for(int p=0;p<=20;p++)
    for(int i=0;i<1<<p;i++)
        if(!(i&(1<<p))) f[i]+=f[i|(1<<p)];

技巧经验

容斥

  • ((0,0))((n,m))且不经过一些禁点的方案数(BZOJ两双手)

    (f[i]=sum_{j=0}^{j<i}-f[j]*way(j,i),f[0]=-1,way(j,i))表示从j点到i点的方案数,按照从((0,0))走到该点的所需步数排序

组合计数

  • 可以画成二维平面上的点去移动,辅助理解、推式子

有趣的式子

gcd有关

  • (sum_{i=1}^{n}gcd(i,n))

    (=sum_{d=1}^{n}(dsum_{i=1}^{n}[gcd(i,n)==d]))

    (=sum_{d=1}^{n}dvarphi(frac{n}{d}))(当然到这步就可以(O(nsqrt n))做了)

    (=sum_{d=1}^{n}frac{n}{d}varphi(d))

    (=sum_{d=1}^{n}frac{n}{d}dprod_{p_i|d}frac{p_i-1}{p_i})

    (=nsum_{d=1}^{n}prod_{p_i|d}frac{p_i-1}{p_i})

    考虑每个质数选或者不选的生成函数:((b_ifrac{p_i-1}{p_i}+1)),其中(b_i)表示(p_i)的指数,含义为选了这个质数就会有(b_i)种指数

    所以最后答案就是(nprod_{p_i|n}(b_ifrac{p_i-1}{p_i}+1))

    ?

以上是关于Noip前的大抱佛脚----数论的主要内容,如果未能解决你的问题,请参考以下文章

Noip前的大抱佛脚----图论

Noip前的大抱佛脚----奇技淫巧

Noip前的大抱佛脚----根号对数算法

Noip前的大抱佛脚----字符串

Noip前的大抱佛脚----一些思路

Noip前的大抱佛脚----数据结构