FlyWhite的ACM模板

FlyWhite的ACM模板

目录

  • 数学

    • 数论部分
      • 欧几里得、扩欧几里得、类欧几里得——————————————2
      • Pollard_rho算法(1/4次方分解质因数)————————————4
      • 逆元————————————————————————————6
      • 欧拉函数——————————————————————————7
      • CRT与EXCRT————————————————————————8
      • 模m的k次根————————————————————————9
      • 拉宾米勒素数测试(c++与大数python)———————————10
      • 常用的数论函数推导与常用卷积———————————————11
      • min_25筛—————————————————————————13
      • 杜教筛——————————————————————————16
      • BSGS———————————————————————————16
      • 扩展BSGS—————————————————————————17
      • Cipolla算法(二次剩余)——————————————————17
      • Pell方程特解————————————————————————18
    • 代数
      • FFT————————————————————————————19
      • NTT————————————————————————————20
      • 多项式求逆元、开根号、求导、积分、ln、exp—————————20
      • 多项式的牛顿迭代—————————————————————22
      • 分治FFT(处理多个低阶多项式相乘)————————————23
      • 常用的麦克劳林展开———————————————————25
      • 拉格朗日插值——————————————————————25
      • Stern-Brocot树(求值大于up分子分母小于n的最简分数)——26
      • 高斯消元————————————————————————27
      • 矩阵求行列式——————————————————————30
    • 组合
      • 组合公式————————————————————————31
      • Lucas与EXLucas(模数为素数和不为素数时的组合数降幂)——31
      • 卡特兰数————————————————————————34
      • Cayley公式———————————————————————36
      • Prufer编码———————————————————————36
      • 矩阵树定理———————————————————————36
    • 其他
      • 辗转相除法求两分数之间分子分母尽可能小的分数——————37
      • 蔡勒公式————————————————————————38
      • BM与广义BM——————————————————————38
      • 单纯形(高纬度线性规划)————————————————44
      • 常用的前缀和公式————————————————————45
  • 图论

    • 网络流
      • 最大流—————————————————————————45
      • 费用流—————————————————————————47
      • zkw费用流———————————————————————48
      • Dijkstra优化费用流———————————————————50
      • 重要建图套路——————————————————————52
    • 最短路———————————————————————————54
  • DP

    • 斜率DP———————————————————————————57
    • 树型DP———————————————————————————58
  • 其他

    • Java大数——————————————————————————60
    • stl收录———————————————————————————61

数学

数论

欧几里得、扩欧几里得、类欧几里得

欧几里得

typedef long long LL ;
LL gcd(LL a,LL b)
{
   return b==0 ? a :gcd(b,a%b);
}

扩欧几里得

inline LL exgcd(LL a,LL b,LL &x,LL &y){
    if(b == 0){
        x = 1,y = 0;
        return a;
    }
    LL g = exgcd(b,a%b,x,y);
    LL tep = x;
    x = y;
    y = tep-a/b*y;
    return g;
}

类欧几里得

∑ x = 1 n ⌊ C + B x A ⌋ \sum_{x=1}^n\left\lfloor\frac{C+Bx}{A}\right\rfloor x=1nAC+Bx

在这里插入图片描述

#define LL __int128
long long Sum(LL n,LL a,LL b,LL c)
{
	
	if(n<=0) return 0;
	LL ans=0;
	if(c<0)
	{
		LL t=(a-c-1)/a;
		c+=a*t;
		ans-=n*t;
	}
	if(b<0)
	{
		LL t=(a-1-b)/a;
		b+=a*t;
		ans-=(n+1)*n*t/2;
	}
	if(c/a>0||b/a>0)
	{
		ans+=(c/a)*n;ans+=(b/a)*n*(n+1)/2;
		c%=a;b%=a;
	}
	LL newn=(b*n+c)/a;
	ans+=newn*(n+1)-Sum(newn,b,a,-c+b-1);
	return ans;
}
#undef LL

∑ i = 1 n ⌊ a i + b c ⌋ ( m o d   m o d ) \sum_{i=1}^n\left\lfloor\frac{ai+b}{c}\right\rfloor(mod\ mod) i=1ncai+b(mod mod)(即有模数)

ll Sum(ll a,ll b,ll c,ll n){
    if (!a) return 0;
    ll x,y;
    if(a>=c||b>=c){
        x=Sum(a%c,b%c,c,n);
        y=((a/c)%mod*(n%mod)%mod*((n+1)%mod)%mod*inv2+b/c%mod*((n+1)%mod)+x)%mod;
        y=(y+mod)%mod;
        return y;
    }
    ll m=((__int128)a*n+b)/c;
    x=Sum(c,c-b-1,a,m-1);
    y=((__int128)n*m-x)%mod;
    y=(y+mod)%mod;
    return y;
}
Pollard_rho算法(1/4次方分解质因数)

复杂度 O ( n 1 4 ) O(n^{1\over 4}) O(n41)大约可以分解1e18一百次

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
 
using namespace std;
typedef long long LL;
 
long long factor[110], cnt;
long long Mul_Mod(long long a, long long b, long long c) {
    if (b == 0)
        return 0;
    long long ans = Mul_Mod(a, b / 2, c);
    ans = (ans * 2) % c;
    if (b % 2)
        ans = (ans + a) % c;
    return ans;
}
long long Pow_Mod(long long a, long long b, long long c) {
    if (b == 0)
        return 1;
    long long x = Pow_Mod(a, b / 2, c);
    if (x == 0)
        return 0;
    long long y = Mul_Mod(x, x, c);
    if (y == 1 && x != 1 && x != c - 1)
        return 0;
    if (b % 2)
        y = Mul_Mod(y, a, c);
    return y;
}
bool Miller_rabin(long long n, int timenum = 10) {
    if (n < 2)
        return false;
    if (n == 2)
        return true;
    while (timenum--) {
        if (Pow_Mod(rand() % (n - 2) + 2, n - 1, n) != 1)
            return false;
    }
    return true;
}
long long Gcd(long long a, long long b) {
    long long t;
    while (b) {
        t = a;
        a = b;
        b = t % b;
    }
    return a;
}
void Pollard(long long n);
 
void Factor(long long n) {
    long long d = 2;
    while (true) {
        if (n % d == 0) {
            Pollard(d);
            Pollard(n / d);
            return;
        }
        d++;
    }
}
void Pollard(long long n) {
    if (n <= 0)
        printf("error\n");
    if (n == 1)
        return;
    if (Miller_rabin(n)) {
        factor[cnt++] = n;
        return;
    }
    long long i = 0, k = 2, x, y, d;
    x = y = rand() % (n - 1) + 1;
    while (i++ < 123456) {
        x = (Mul_Mod(x, x, n) + n - 1) % n;
        d = Gcd((y - x + n) % n, n);
        if (d != 1) {
            Pollard(d);
            Pollard(n / d);
            return;
        }
        if (i == k) {
            y = x;
            k *= 2;
        }
    }
    Factor(n);
}
 
 
int main() {
    int Case;
    long long n;
    scanf("%d", &Case);
    while (Case--) {
        scanf("%lld", &n);
        if (Miller_rabin(n))
            printf("Prime\n");
        else {
            cnt = 0;
            Pollard(n);
            sort(factor, factor + cnt);
            for(int i=0;i<cnt;i++) 
            printf("%lld\n", factor[i]);
        }
    }
    return 0;
}

逆元

扩欧求逆元

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;
}

递推求逆元

LL inv[mod+5];
void getInv(LL mod)
{
    inv[1]=1;
    for(int i=2;i<mod;i++)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
欧拉函数

欧拉定理扩展:小于n与n互质的数的和为 ϕ ( n ) ∗ n / 2 \phi(n)*n/2 ϕ(n)n/2

欧拉函数的通项公式 ϕ ( x ) = x ∏ i = 1 n ( 1 − 1 p i ) \phi(x)=x\prod_{i=1}^n(1-\frac{1}{p_i}) ϕ(x)=xi=1n(1pi1)其中p为x的质因数

log(n)求单个值

int oula(int n)
{
    int rea=n;
    for(int i=2; i*i<=n; i++)
        if(n%i==0)
        {
            rea=rea-rea/i;
            do
                n/=i;
            while(n%i==0);
        }
    if(n>1)
        rea=rea-rea/n;
    return rea;
}

素数筛

void euler(int n)
{
	phi[1]=1;
	for (int i=2;i<=n;i++)
	{
		if (flag[i]==0)
		{
			prime[++num]=i;
			phi[i]=i-1;
		}
		for (int j=1;j<=num&&prime[j]*i<=n;j++)
		{
			flag[i*prime[j]]=1;
			if (i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*phi[prime[j]];
		}
	}
}

CRT与EXCRT

CRT

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

int china()
{
    int ans=0,lcm=1,x,y;
    for(int i=1;i<=k;++i) lcm*=b[i];
    for(int i=1;i<=k;++i)
    {
        int tp=lcm/b[i];
        exgcd(tp,b[i],x,y);
        x=(x%b[i]+b[i])%b[i];//x要为最小非负整数解
        ans=(ans+tp*x*a[i])%lcm;
    }
    return (ans+lcm)%lcm;
}

EXCRT

LL work(){
    LL M=m[1],A=a[1],t,d,x,y;int i;
    for(i=2;i<=n;i++){
        d=exgcd(M,m[i],x,y);
        if((a[i]-A)%d)return -1;
        x*=(a[i]-A)/d,t=m[i]/d,x=(x%t+t)%t;
        A=M*x+A,M=M/d*m[i],A%=M;
    }
    A=(A%M+M)%M;
    return A;
}
模m的k次根
// How to calculate x^k (mod p) =  
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
void extgcd(long long a,long long b,long long &x,long long &y)
{
    if(b == 0) x = 1,y = 0;
    else{
        extgcd(b,a%b,y,x);
        y -= a/b*x;
    }
}
long long Phi(LL n)
{
   long long ans = n;
   for(int i = 2;i < n; ++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;
}
long long qpow(long long a,long long b,long long m){
    long long ans = 1;
    a %= m;
    while(b>0){
        if(b&1) ans = ans*a%m;
        a = a*a%m;
        b >>= 1;
    }
    return ans;
}

int main(void)
{
//  init();
   LL x,k,b,m;
   LL xx,yy;
   while(cin>>k>>b>>m){
        long long Phim = Phi(m);
//          cout<<Phim<<endl;
        extgcd(k,Phim,xx,yy); 
        xx = (xx%Phim+Phim)%Phim;
        cout<<xx<<endl;
        x = qpow(b,xx,m);
        cout<<x<<endl;
        cout<<qpow(x,k,m)<<endl;
   }
   return 0;
}
拉宾米勒素数测试(c++与大数python)

c++

 // 18位素数:154590409516822759
// 19位素数:2305843009213693951 (梅森素数)
// 19位素数:4384957924686954497
LL prime[6] = {2, 3, 5, 233, 331};
LL qmul(LL x, LL y, LL mod) { // 乘法防止溢出, 如果p * p不爆LL的话可以直接乘; O(1)乘法或者转化成二进制加法
    return (x * y - (long long)(x / (long double)mod * y + 1e-3) *mod + mod) % mod;
}
LL qpow(LL a, LL n, LL mod) {
    LL ret = 1;
    while(n) {
        if(n & 1) ret = qmul(ret, a, mod);
        a = qmul(a, a, mod);
        n >>= 1;
    }
    return ret;
}
bool Miller_Rabin(LL p) {
    if(p < 2) return 0;
    if(p != 2 && p % 2 == 0) return 0;
    LL s = p - 1;
    while(! (s & 1)) s >>= 1;
    for(int i = 0; i < 5; ++i) {
        if(p == prime[i]) return 1;
        LL t = s, m = qpow(prime[i], s, p);
        while(t != p - 1 && m != 1 && m != p - 1) {
            m = qmul(m, m, p);
            t <<= 1;
        }
        if(m != p - 1 && !(t & 1)) return 0;
    }
    return 1;
}

python

import random

def QuickPow(a, b, MOD):
    ret = 1
    tmp = a%MOD
    while b>0:
        if (b&1):
            ret = (ret*tmp)%MOD
        tmp = (tmp*tmp)%MOD
        b >>= 1
    return ret

def Miller_Rabin(a, p): # a^(p-1) = 1 (mod p) 
    p1 = p-1
    s2 = p1 & -p1 # fetch the last 1
    x = QuickPow(a, p1//s2, p)
    if x == 1 or x == p1:
        return True
    while s2>1:
        x = (x*x)%p
        if x == 1:
            return False
        if x == p1:
            return True
        s2 >>= 1
    return False

def IsPrime(p, k):
    if p == 2 or p == 3:
        return True
    if p < 2 or (p&1) == 0:
        return False
    for i in range(k):
        if not Miller_Rabin(random.randint(2, p-1), p):
            return False
    return True

n = int(input())
for i in range(n):
    p = int(input())
    print('YES' if IsPrime(p, 5) else 'NO')
常用的数论函数推导与常用卷积

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] = ∑ i = 1 n ∑ j = 1 m ∑ p ∣ g c d ( i d , j d ) μ ( p ) \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d]=\sum_{i=1}^n\sum_{j=1}^m\sum_{p|gcd(\frac{i}{d},\frac{j}{d})}\mu(p) i=1nj=1m[gcd(i,j)=d]=i=1nj=1mpgcd(di,dj)μ(p)
2.
莫比乌斯函数卷积单位函数为欧拉函数
3.
∑ i = 1 n g c d ( n , i ) = ∑ d ∣ n d φ ( n d ) \sum_{i=1}^{n}gcd(n,i)=\sum_{d|n}d\varphi({n\over d}) i=1ngcd(n,i)=dndφ(dn)

∑ i = 1 n ∑ j = 1 n i ⋅ j [ g c d ( i , j ) = 1 ] = ∑ i = 1 n i 2 φ ( i ) \sum_{i=1}^n\sum_{j=1}^ni\cdot j[gcd(i,j)=1]=\sum_{i=1}^ni^2\varphi(i) i=1nj=1nij[gcd(i,j)=1]=i=1ni2φ(i)

  1. 莫比乌斯函数卷积恒等函数为元函数

μ ∗ I = e \mu*I=e μI=e
也就是说 I I I μ \mu μ互为逆元

  1. 欧拉函数卷积恒等函数为单位函数

φ ∗ I = i d \varphi*I=id φI=id

同理也就有
φ = i d ∗ μ \varphi=id*\mu φ=idμ

根据这个式子可以推出另一个非常巧妙的公式
ϕ ( n ) n = ∑ d ∣ n μ ( d ) d \frac{\phi (n)}{n}=\sum_{d|n}\frac{\mu(d)}{d} nϕ(n)=dndμ(d)

  1. 恒等函数卷积单位函数为约数和函数

I ∗ i d = σ I*id=\sigma Iid=σ

同理也就有
i d = σ ∗ μ id=\sigma*\mu id=σμ

  1. 恒等函数卷积恒等函数为约数个数函数

I ∗ I = d I*I=d II=d

同理也就有
I = d ∗ μ I=d*\mu I=dμ

( i d ⋅ φ ) ∗ i d = i d 2 (id\cdot \varphi)*id=id^2 (idφ)id=id2

  1. rng_58-clj等式

∑ i = 1 a ∑ j = 1 b ∑ k = 1 c d ( i j k ) = ∑ g c d ( i , j , k ) = 1 ⌊ a i ⌋ ⌊ b j ⌋ ⌊ c k ⌋ \sum_{i=1}^a\sum_{j=1}^b\sum_{k=1}^cd(ijk)=\sum_{gcd(i,j,k)=1} \left\lfloor{a\over i}\right\rfloor\left\lfloor{b\over j}\right\rfloor\left\lfloor{c\over k}\right\rfloor i=1aj=1bk=1cd(ijk)=gcd(i,j,k)=1iajbkc

  1. 小于等于n与n互素的数之和
    ∑ i = 1 n [ g c d ( i , n ) = 1 ] i = 1 2 ( [ n = 1 ] + n φ ( n ) ) \sum_{i=1}^n[gcd(i,n)=1]i=\frac{1}{2}([n=1]+n\varphi(n)) i=1n[gcd(i,n)=1]i=21([n=1]+nφ(n))

  2. 1-n中的数与n的gcd的和

∑ i = 1 n g c d ( i , n ) = ∑ d ∣ n d φ ( n d ) \sum_{i=1}^ngcd(i,n)=\sum_{d|n}d\varphi(\frac{n}{d}) i=1ngcd(i,n)=dndφ(dn)

  1. 欧拉函数=欧拉函数卷积莫比乌斯函数卷积恒等函数

φ = φ ∗ μ ∗ I \varphi=\varphi*\mu*I φ=φμI

对于欧拉函数卷积莫比乌斯函数有递推式

if(i%Prime[j]==0)
{
	if((i/Prime[j])%Prime[j]==0) muphi[i*Prime[j]]=muphi[i]*Prime[j];
	else muphi[i*Prime[j]]=(Prime[j]-1)*(Prime[j]-1)*muphi[i/Prime[j]];
	break;
}
else 
{
	muphi[i*Prime[j]]=muphi[i]*muphi[Prime[j]];	
}
min_25筛

定义一个函数
c s l ( p , x ) = { 3 e + 1 x = p e   a n d   ∃ a , b   p = a 2 + b 2 1 x = p e   a n d   ∄ a , b   p = a 2 + b 2 0 o t h e r csl(p,x)=\begin{cases} 3e+1& x=p^e\ and\ \exist a,b\ p=a^2+b^2\\ 1& x=p^e\ and\ \not\exist a,b\ p=a^2+b^2\\ 0 & other \end{cases} csl(p,x)=3e+110x=pe and a,b p=a2+b2x=pe and a,b p=a2+b2other
再定义 t l ( p , x ) = m a x d ∣ x ( c s l ( p , d ) ) tl(p,x)=max_{d|x}(csl(p,d)) tl(p,x)=maxdx(csl(p,d))

现在要求求
∑ d = 1 n ∏ p t l ( p , d ) \sum_{d=1}^n\prod_ptl(p,d) d=1nptl(p,d)

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long LL;
int s,n;
const int size=1e5+5;
int pre[size][4],p[size];
bool prime[size];
int tot;
int num[size];
int h[size][4];
int tol;
inline int ID(LL x){return x<=s?x:tol-n/x+1;}//x should be LL
inline int f(int p,int e)
{
    if(p%4==1) return 3*e+1;
    return 1;
}
LL get_g(int x,int k)
{
    if(x<=1||p[k]>x) return 0;
    LL ans=h[ID(x)][3]-pre[k-1][3]+(h[ID(x)][1]-pre[k-1][1])*4;
    if(k==1) ans++;//'2' is specil
    while(1LL*p[k]*p[k]<=x&&k<=tot)
    {
        int pk=p[k],t=p[k];
        for(int e=1;t*pk<=x;e++,t=t*pk)
            ans=ans+f(pk,e)*(get_g(x/t,k+1))+f(pk,e+1);
        k++;
    }
    return ans;
}  
void get_h(int n)
{
    s=sqrt(n);
    tot=0;
    while(s*s<=n) s++;
    while(s*s>n) s--;
    for(int j=0;j<4;j++) pre[0][j]=0;
    for(int i=1;i<=s;i++) prime[i]=true;
    for(int i=2;i<=s;i++)
    {
        if(prime[i])
        {
            p[++tot]=i;
            for(int j=0;j<4;j++) pre[tot][j]=pre[tot-1][j]+(i%4==j);
        }
        for(int j=1;j<=tot&&p[j]*i<=s;j++)
        {
            prime[i*p[j]]=false;
            if(i%p[j]==0) break;
        }
    }
    tol=0;
    for(int i=1;i<=s;i++) num[++tol]=i;
    for(int i=s;i>=1;i--) if(n/i>s) num[++tol]=n/i;
    for(int i=1;i<=tol;i++)
    {
        h[i][1]=num[i]/4+(num[i]%4>=1)-1;
        h[i][3]=num[i]/4+(num[i]%4>=3);
        h[i][2]=num[i]/4+(num[i]%4>=2);
        h[i][0]=num[i]/4;
    }
    int x=1;
    for(int j=1;j<=tot;j++)
    {
        while(num[x]<p[j]*p[j]) x++;
        for(int i=tol;i>=x;i--)
        {
            for(int r=0;r<4;r++)
            {
                h[i][r*p[j]%4]=h[i][r*p[j]%4]-(h[ID(num[i]/p[j])][r]-pre[j-1][r]);
            }
        }
    }
}
int32_t main()
{
    int t;
    //freopen("k.in","r",stdin);
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld",&n);
        get_h(n);
        printf("%lld\n",get_g(n,1)+1);
    }
}

二平方定理:一个素数可以分解成两个平方数相加,当且仅当这个数字是2的倍数或者模4余1时

杜教筛

求莫比乌斯函数前缀和

int summu(int x)
{
	if(x<size) return sumarr[x];
	if(mp.count(x))return  mp[x];
	int ans=1;
	for(int l=2,r;l<=x;l=r+1)
	{
		r=x/(x/l);
		ans=(ans-1LL*(r-l+1)*summu(x/r)%mod+mod)%mod;
	}
	return mp[x]=ans;
}

注意:杜教筛通常需要线性筛处理出小数据的前缀和,以防止超时

BSGS

Q:由BSGS::init(int p,int b,int BS_size_son,int BS_size_mon)预处理之后由BSGS::calc(N)可以计算满足 B k ≡ N ( m o d   p ) B^k\equiv N(mod\ p) BkN(mod p),复杂度为 O ( p B S s i z e s o n B S s i z e m o n + p q ( 1 − B S s i z e s o n B S s i z e m o n ) O(p^{\frac{BS_size_son}{BS_size_mon}}+p^{q(1-\frac{BS_size_son}{BS_size_mon})} O(pBSsizemonBSsizeson+pq(1BSsizemonBSsizeson)其中q是需要求解的N的个数

namespace BSGS{
	#define int long long 
	unordered_map<int,int> mp;
	int loop,up;
	int P,B;
	int BS_size_son,BS_size_mon;
	void init(int p,int b,int BS_size_son_,int BS_size_mon_)
	{
		BS_size_son=BS_size_son_;
		BS_size_mon=BS_size_mon_;
		P=p,B=b;
		mp.clear();
		up=ceil(pow(p,1.0*BS_size_son/BS_size_mon));
		int t=1;
		for(int i=0;i<=up;i++) 
		{
			if(i==up) loop=t;
			mp[t]=i;
			t=1LL*t*b%p;
		}
	}
	int calc(int N)
	{
		int m=ceil(pow(p,1.0-1.0*BS_size_son/BS_size_mon));
		int obj=quick_pow(N,P-2,P);
		for(int i=1;i<=m;i++)
		{
			obj=1LL*obj*loop%P;
			if(mp.count(obj))
			{
				return 1LL*i*up-mp[obj];
			}
		}
		return -1;
	}
	#undef int
}

注意:对于询问较多时,可以先预处理所有的大步中的数值,并通过合理地调整大步的大小使得复杂度合适

扩展BSGS

求解满足 a x ≡ b ( m o d   p ) a^x\equiv b(mod\ p) axb(mod p)的最小x,p不一定为素数

typedef long long ll;
unordered_map<ll,int> Hash;
int quick_pow(int a,int b,int mod){int ans=1;while(b){if(b&1) ans=1LL*ans*a%mod;a=1LL*a*a%mod;b>>=1;} return ans;}
int exbsgs(int a, int b, int p) {
    a %= p; b %= p;
    if(b == 1) return 0; int cnt = 0; ll t = 1;
    if(a == 0) return b > 1 ? -1 : b == 0 && p > 1;
    for(int g = __gcd(a, p); g != 1; g = __gcd(a, p)) {
        if(b % g) return -1;
        p /= g; b /= g; t = 1ll * t * (1ll * a / g % p) % p;
        cnt++; if(b == t) return cnt;
    }
    Hash.clear(); int m = ceil(sqrt(p));
    ll u = b; for(int i = 0; i < m; i++, u = 1ll * u * a % p) Hash[u] = i;
    ll v = t, iv = quick_pow(a, m, p);
    for(int i = 0; i <= m; i++) {
        v = 1ll * v * iv % p;
        if(Hash.count(v)) return (i + 1) * m - Hash[v] + cnt;
    }
    return -1;
}
Cipolla算法(二次剩余)

Cipolla::calc(n,p)计算n在模p意义下开根号的值

namespace Cipolla{
    LL w,P;
    struct E{
        LL x,y;
        E(LL _x,LL _y):x(_x),y(_y){}
        friend E operator *(E a,E b){
            return E((a.x*b.x%P+a.y*b.y%P*w)%P,(a.x*b.y%P+a.y*b.x%P)%P);
        }
    };
    inline E Pow(E x,int y){
        E ret=E(1,0);
        for(;y;y>>=1,x=x*x) if(y&1) ret=ret*x;
        return ret;
    }
    LL calc(LL x,LL p){
        P=p; x%=P;
        if(x==0) return 0;
        LL tmp=quick_pow(x,(p-1)/2,p);
        if(tmp!=1) return -1;
        while(1){
            tmp=rand()%p;
            LL cur=(tmp*tmp%P+P-x)%P;
            if(quick_pow(cur,(p-1)/2,p)==p-1) break;
        }
        w=(tmp*tmp%P+P-x)%P;
        E A=E(tmp,1);
        return Pow(A,(p+1)/2).x;
    }
}
Pell方程特解

求解 x 2 − n y 2 = 1 x^2-ny^2=1 x2ny2=1的最小正整数根

ULL A,B,p[maxn],q[maxn],a[maxn],g[maxn],h[maxn];
int main()
{
	int n;
	for (int test=1;scanf("%d",&n) && n;++test)
	{
		printf("Case %d: ",test);
		if (fabs(sqrt(n)-floor(sqrt(n)+1e-7))<=1e-7)  
		{
			int a=(int)(floor(sqrt(n)+1e-7));
			printf("%d %d\n",a,1);
		}else
		{
			//求x^2-ny^2=1的最小正整数根,n不是完全平方数 
			p[1]=1;p[0]=0;
			q[1]=0;q[0]=1;
			a[2]=(int)(floor(sqrt(n)+1e-7));
			g[1]=0;h[1]=1;
			for (int i=2;i;++i)
			{
				g[i]=-g[i-1]+a[i]*h[i-1];
				h[i]=(n-sqr(g[i]))/h[i-1];
				a[i+1]=(g[i]+a[2])/h[i];
				p[i]=a[i]*p[i-1]+p[i-2];
				q[i]=a[i]*q[i-1]+q[i-2];
				if (sqr((ULL)(p[i]))-n*sqr((ULL)(q[i]))==1)
				{
					A=p[i];B=q[i];
					break;
				}
			}
			cout << A << ' ' << B <<endl;
		}
	}
	return 0;
}

代数

FFT
namespace Polynomial_multiplication{
    int n, m, rev[size << 2];
    cp a[size << 2], b[size << 2];
    void init(int len) {
        for (n = 1, m = 0; n <= len; n <<= 1, m++);
        for (int i = 0; i < n; ++i) {
            rev[i] = rev[i >> 1] >> 1 | (i & 1) << (m - 1);
            a[i] = cp(0, 0);
            b[i] = cp(0, 0);
        }
    }
    void builda(vector<int> x,int len){for(int i=0;i<=len;i++) a[i]=cp(x[i],0);}
    void builda(int x[],int len){for(int i=0;i<=len;i++) a[i]=cp(x[i],0);}
    void buildb(vector<int> x,int len){for(int i=0;i<=len;i++) b[i]=cp(x[i],0);}
    void buildb(int x[],int len){for(int i=0;i<=len;i++) b[i]=cp(x[i],0);}
    void fft(cp *a, int f) {
        for (int i = 0; i < n; ++i)if (i < rev[i])swap(a[i], a[rev[i]]);
        for (int i = 1; i < n; i <<= 1) {
            double alpha = pi / i;
            if (f == -1)alpha = -pi / i;
            for (int k = 0; k < i; ++k) {
                cp w = cp(cos(alpha*k), sin(alpha*k));
                for (int j = k; j < n; j += (i << 1)) {
                    cp x = w * a[j + i];
                    a[j + i] = a[j] - x;
                    a[j] += x;
                }
            }
        }
        if(f==-1) for(int i=0;i<n;i++) a[i]/=n;
    }
    void calc(vector<int> &v,int len) {
        fft(a, 1); fft(b, 1);
        for (int i = 0; i < n; ++i)a[i] *= b[i];
        fft(a, -1);
        for(int i=0;i<=len;i++) v.push_back(LL(a[i].real()+0.5)%mod);
    }
} 
NTT
void ntt(int a[],int n,int inv)
{
	int bit=0;
	while((1<<bit)<n) bit++;
	for(int i=0;i<n;i++)
	{
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
		if(i<rev[i]) swap(a[i],a[rev[i]]);
	}
	for(int mid=1;mid<n;mid*=2)
	{
		int e=quick_pow(g,(mod-1)/(2*mid));
		for(int i=0;i<n;i+=mid*2)
		{
			int omega=1;
			for(int j=0;j<mid;j++,omega=1LL*omega*e%mod)
			{
				int x=a[i+j],y=1LL*a[i+j+mid]*omega%mod;
				a[i+j]=(x+y)%mod;a[i+j+mid]=(x-y+mod)%mod;
			}
		}
	}
	if(inv==1) return ;
	int nv=quick_pow(n,mod-2);reverse(a+1,a+n);
	for(int i=0;i<n;i++)a[i]=1LL*a[i]*nv%mod;
}
多项式求逆元、开根号、求导、积分、ln、exp
namespace polynomial{
    const int mod=998244353;
    const int g=3;
    int rev[size<<2];
    int A[size<<2],B[size<<2],C[size<<2],D[size<<2],E[size<<2],F[size<<2];
    int vinv[size<<2];
    void getinv(){vinv[1]=1;for(int i=2;i<(size<<2);i++) vinv[i]=1LL*(mod-mod/i)*vinv[mod%i]%mod;}
    int quick_pow(int a,int b){int ans=1;while(b){if(b&1) ans=1LL*ans*a%mod;a=1LL*a*a%mod;b>>=1;} return ans;}
    void NTT(int a[],int n,int inv)
    {
        for(int i=0;i<n;i++)
            if(i<rev[i]) swap(a[i],a[rev[i]]);
        for(int mid=1;mid<n;mid*=2)
        {
            int e=quick_pow(g,(mod-1)/(2*mid));
            for(int i=0;i<n;i+=mid*2)
            {
                int omega=1;
                for(int j=0;j<mid;j++,omega=1LL*omega*e%mod)
                {
                    int x=a[i+j],y=1LL*a[i+j+mid]*omega%mod;
                    a[i+j]=(x+y)%mod;a[i+j+mid]=(x-y+mod)%mod;
                }
            }
        }
        if(inv==1) return ;
        int nv=quick_pow(n,mod-2);reverse(a+1,a+n);
        for(int i=0;i<n;i++)a[i]=1LL*a[i]*nv%mod;
    }
    void inv(int a[],int b[],int n)
    {
        b[0]=quick_pow(a[0],mod-2);
        int len,lim;
        for(len=1;len<(n<<1);len<<=1)
        {
            lim=len<<1;
            for(int i=0;i<len;i++) A[i]=a[i],B[i]=b[i];
            for(int i=0;i<lim;i++) rev[i]=rev[i>>1]>>1|((i&1)?len:0);
            NTT(A,lim,1);NTT(B,lim,1);
            for(int i=0;i<lim;i++) b[i]=((2-1LL*A[i]*B[i]%mod)*B[i]%mod+mod)%mod;
            NTT(b,lim,-1);
            for(int i=len;i<lim;i++) b[i]=0;
        }
        for(int i=0;i<len;i++) A[i]=B[i]=0;
        for(int i=n;i<len;i++) b[i]=0;
    }
    void sqrt(int a[],int b[],int n)
    {
        b[0]=1;
        int *A=C,*B=D,len,lim;
        for(len=1;len<(n<<1);len<<=1)
        {
            lim=len<<1;
            for(int i=0;i<len;i++) A[i]=a[i];
            inv(b,B,lim>>1);
            for(int i=0;i<lim;i++) rev[i]=rev[i>>1]>>1|((i&1)?len:0);
            NTT(A,lim,1);NTT(B,lim,1);
            for(int i=0;i<lim;i++) A[i]=1LL*A[i]*B[i]%mod;
            NTT(A,lim,-1);
            for(int i=0;i<len;i++) b[i]=1LL*(b[i]+A[i])%mod*(mod+1)/2%mod;
            for(int i=len;i<lim;i++) b[i]=0;
        }
        for(int i=0;i<len;i++) A[i]=B[i]=0;
        for(int i=n;i<len;i++) b[i]=0;
    }
	void differential(int a[],int b[],int n)
	{
		for(int i=0;i<n-1;i++)
			b[i]=1LL*a[i+1]*(i+1)%mod;
		b[n-1]=0;		
	}
	void integral(int a[],int b[],int n)
	{
		for(int i=1;i<n+1;i++)
			b[i]=1LL*a[i-1]*vinv[i]%mod;
		b[0]=0;
	}
    void ln(int a[],int b[],int n)
    {
    	int *A=C,*B=D,len=1;
    	inv(a,B,n);
    	differential(a,A,n);
    	while(len<(n<<1)) len<<=1;
    	NTT(A,len,1),NTT(B,len,1);
    	for(int i=0;i<len;i++) A[i]=1LL*A[i]*B[i]%mod;
    	NTT(A,len,-1);
    	integral(A,b,n); 
    }
    void exp(int a[],int b[],int n)
    {
    	b[0]=1;
    	int *A=E,*B=F,len,lim;
    	for(len=1;len<(n<<1);len<<=1)
    	{
    		lim=len<<1;
    		for(int i=0;i<len;i++) A[i]=b[i];
    		ln(b,B,lim>>1);
    		B[0]=(1-B[0]+a[0]+mod)%mod;
    		for(int i=1;i<len;i++) B[i]=(-B[i]+a[i]+mod)%mod;
    		for(int i=0;i<lim;i++) rev[i]=rev[i>>1]>>1|((i&1)?len:0);
            NTT(A,lim,1);NTT(B,lim,1);
            for(int i=0;i<lim;i++) b[i]=1LL*A[i]*B[i]%mod;
            NTT(b,lim,-1);
            for(int i=len;i<lim;i++) b[i]=0;
        }
    	for(int i=0;i<len;i++) A[i]=B[i]=0;
        for(int i=n;i<len;i++) b[i]=0;
    }
}
多项式的牛顿迭代

已知 G ( x ) G(x) G(x),求解 G ( F ( x ) ) ≡ 0 ( m o d   x n ) G(F(x))\equiv 0(mod\ x^n) G(F(x))0(mod xn)

假设存在一个函数H使得有 G ( H ( x ) ) ≡ 0 ( m o d   x n 2 ) G(H(x))\equiv 0(mod\ x^{n\over2}) G(H(x))0(mod x2n)

对G(F(x))在H(x)处泰勒展开

由于 G ( H ( x ) ) ≡ 0 ( m o d   x n 2 ) G(H(x))\equiv 0(mod\ x^{n\over 2}) G(H(x))0(mod x2n)代表了F(x)的前 n 2 n\over2 2n项就是H(x)故也就有
( F ( x ) − H ( x ) ) 2 ≡ 0 ( m o d   x n ) (F(x)-H(x))^2\equiv 0(mod\ x^n) (F(x)H(x))20(mod xn)
故泰勒展开也就可以写作
G ( F ( x ) ) ≡ G ( H ( x ) ) + G ′ ( H ( x ) ) ( F ( x ) − H ( x ) ) ≡ 0 ( m o d   x n ) G(F(x))\equiv G(H(x))+G'(H(x))(F(x)-H(x))\equiv 0(mod\ x^n) G(F(x))G(H(x))+G(H(x))(F(x)H(x))0(mod xn)
移项变换之后就有
F ( x ) ≡ H ( x ) − G ( H ( x ) ) G ′ ( H ( x ) ) ( m o d   x n ) F(x)\equiv H(x)-\frac{G(H(x))}{G'(H(x))}(mod\ x^n) F(x)H(x)G(H(x))G(H(x))(mod xn)
就可以通过不断缩小n得到答案

分治FFT

solve(1,1,n)求
( x + a S 1 ) ( x + a S 2 ) . . . ( x + a S n ) (x+a^{S_1})(x+a^{S_2})...(x+a^{S_n}) (x+aS1)(x+aS2)...(x+aSn)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long long ll;
const double pi=acos(-1);
typedef complex<double> cp;
int n,a,q;
const int mod=100003;
const int size=1e5+5;
vector<int> co[size<<2];
cp x[size],y[size];
int b[size],s[size];
int quick_pow(int a,int b){int ans=1;while(b){if(b&1) ans=1LL*a*ans%mod;a=1LL*a*a%mod;b>>=1;} return ans;}
int fac[size];
int invfac[size];
void init()
{
	fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=1LL*fac[i-1]*i%mod;
	invfac[n]=quick_pow(fac[n],mod-2);
	for(int i=n-1;i>=0;i--) invfac[i]=1LL*invfac[i+1]*(i+1)%mod;
}
namespace Polynomial_multiplication{
	int n, m, rev[size << 2];
	cp a[size << 2], b[size << 2];
	void init(int len) {
		for (n = 1, m = 0; n <= len; n <<= 1, m++);
		for (int i = 0; i < n; ++i) {
			rev[i] = rev[i >> 1] >> 1 | (i & 1) << (m - 1);
			a[i] = cp(0, 0);
			b[i] = cp(0, 0);
		}
	}
	void builda(vector<int> x,int len){for(int i=0;i<=len;i++) a[i]=cp(x[i],0);}
	void builda(int x[],int len){for(int i=0;i<=len;i++) a[i]=cp(x[i],0);}
	void buildb(vector<int> x,int len){for(int i=0;i<=len;i++) b[i]=cp(x[i],0);}
	void buildb(int x[],int len){for(int i=0;i<=len;i++) b[i]=cp(x[i],0);}
	void fft(cp *a, int f) {
		for (int i = 0; i < n; ++i)if (i < rev[i])swap(a[i], a[rev[i]]);
		for (int i = 1; i < n; i <<= 1) {
			double alpha = pi / i;
			if (f == -1)alpha = -pi / i;
			for (int k = 0; k < i; ++k) {
				cp w = cp(cos(alpha*k), sin(alpha*k));
				for (int j = k; j < n; j += (i << 1)) {
					cp x = w * a[j + i];
					a[j + i] = a[j] - x;
					a[j] += x;
				}
			}
		}
		if(f==-1) for(int i=0;i<n;i++) a[i]/=n;
	}
	void calc(vector<int> &v,int len) {
		fft(a, 1); fft(b, 1);
		for (int i = 0; i < n; ++i)a[i] *= b[i];
		fft(a, -1);
		for(int i=0;i<=len;i++) v.push_back(LL(a[i].real()+0.5)%mod);
	}
} 
void solve(int id,int l,int r)
{
	if(l==r)
	{
		co[id].push_back(1);
		co[id].push_back(b[l]);
		return ;
	}
	int mid=(l+r)/2;
	solve(id<<1,l,mid);
	solve(id<<1|1,mid+1,r);
	Polynomial_multiplication::init(r-l+1);
	Polynomial_multiplication::builda(co[id<<1],mid-l+1);
	Polynomial_multiplication::buildb(co[id<<1|1],r-mid);
	Polynomial_multiplication::calc(co[id],r-l+1);
}

int ans[size];
inline LL combi(int k,int n){return k>n?0:1LL*fac[n]*invfac[k]%mod*invfac[n-k]%mod;}
int main()
{
	scanf("%d%d%d",&n,&a,&q);
	init();
	for(int i=1;i<=n;i++) scanf("%d",&s[i]);
	for(int i=1;i<=n;i++) b[i]=quick_pow(a,s[i]%(mod-1));
	solve(1,1,n);
	int inva_1=quick_pow(a-1,mod-2);
	for(int i=1;i<=n;i++) ans[i]=(co[1][i]-combi(i,n)+mod)%mod*inva_1%mod;
	for(int i=1;i<=q;i++)
	{
		int k;
		scanf("%d",&k);
		printf("%d\n",ans[k]);
	}
	return 0;
}

常用的麦克劳林展开

1 + x + x 2 2 ! + x 3 3 + . . . = e x 1 − x + x 2 2 ! − x 3 3 + . . = e − x 1 + x 2 2 ! + x 4 4 ! + x 6 6 ! + . . = e x + e − x 2 x + x 3 3 ! + x 5 5 ! + x 7 7 ! + . . = e x − e − x 2 x − 1 2 x 2 + 1 3 x 3 + . . . = l n ( 1 + x ) x − 1 3 ! x 3 + 1 5 ! x 5 + . . . = s i n ( x ) x + 1 2 x 3 3 + 1 ⋅ 3 2 ⋅ 4 x 5 5 + 1 ⋅ 3 ⋅ 5 2 ⋅ 4 ⋅ 6 x 7 7 + . . . = a r c s i n ( x ) 1 − 1 2 ! x 2 + 1 4 ! x 4 + . . . = c o s ( x ) 1 + a 1 ! x + a ( a − 1 ) 2 ! x 2 + a ( a − 1 ) ( a − 2 ) 3 ! x 3 + . . . = ( x + 1 ) a \begin{aligned} &1+x+\frac{x^2}{2!}+\frac{x^3}{3}+...=e^x\\ &1-x+\frac{x^2}{2!}-\frac{x^3}{3}+..=e^{-x}\\ &1+\frac{x^2}{2!}+\frac{x^4}{4!}+\frac{x^6}{6!}+..=\frac{e^x+e^{-x}}{2}\\ &x+\frac{x^3}{3!}+\frac{x^5}{5!}+\frac{x^7}{7!}+..=\frac{e^x-e^{-x}}{2}\\ &x-\frac{1}{2}x^2+\frac{1}{3}x^3+...=ln(1+x)\\ &x-\frac{1}{3!}x^3+\frac{1}{5!}x^5+...=sin(x)\\ &x+\frac{1}{2}\frac{x^3}{3}+\frac{1\cdot3}{2\cdot 4}\frac{x^5}{5}+\frac{1\cdot3\cdot5}{2\cdot4\cdot6}\frac{x^7}{7}+...=arcsin(x)\\ &1-\frac{1}{2!}x^2+\frac{1}{4!}x^4+...=cos(x)\\ &1+\frac{a}{1!}x+\frac{a(a-1)}{2!}x^2+\frac{a(a-1)(a-2)}{3!}x^3+...=(x+1)^a \end{aligned} 1+x+2!x2+3x3+...=ex1x+2!x23x3+..=ex1+2!x2+4!x4+6!x6+..=2ex+exx+3!x3+5!x5+7!x7+..=2exexx21x2+31x3+...=ln(1+x)x3!1x3+5!1x5+...=sin(x)x+213x3+24135x5+2461357x7+...=arcsin(x)12!1x2+4!1x4+...=cos(x)1+1!ax+2!a(a1)x2+3!a(a1)(a2)x3+...=(x+1)a

拉格朗日插值

已知n次多项式前n+1项,求多项式其余任意项数,和已知前n+2项,求前缀和

namespace lagrange{//calculate k-degree polynomial Prefix sum of n or k_degree poly' value of n
	const int maxk=1000+5;//the order of the polynomial
	int coeff[maxk];int suf[maxk],bef[maxk];
	int mod;
	int k;
	void init_sum(int k_,int f[],int mod_) //need prefix k+2' value
	{
		mod=mod_;
		k=k_+2;
	    for(int i=1;i<=k;i++) coeff[i]=f[i];
	    coeff[0]=0;
	    for(int i=1;i<=k;i++) coeff[i]=(coeff[i-1]+coeff[i])%mod;
	}
	void init_val(int k_,int f[],int mod_) //need prefix k+1's value
	{
		mod=mod_;
		k=k_+1;
		for(int i=1;i<=k;i++) coeff[i]=f[i];
	}
	int calc(int n)
	{
		if(n==0) return 0;
	    if(n<=k) return coeff[n];
	    bef[0] = suf[0] = 1;
		for (int i = 1; i <= k ; ++i) {
			bef[i] = 1LL*bef[i - 1] * ((n - i) % mod) % mod;
			suf[i] = 1LL*suf[i - 1] * ((n + i - k - 1) % mod) % mod;
		}
		int res = 0;
		for (int i = 1; i <= k ; ++i) {
			int s = 1LL*coeff[i] * bef[i - 1] % mod * suf[k - i ] % mod * inv[i - 1] % mod * inv[k - i] % mod;// is facinv
			if ((k - i) & 1)s = -s;
			res += s;
			res = (res % mod + mod) % mod;
		}
		return res;
	}
}

对于n-1次多项式已知n个不相邻的点的点值,则可以由

f ( X ) = ∑ i = 0 n − 1 y i ∏ j ≠ i X − x j x i − x j f(X)=\sum_{i=0}^{n-1}y_i\prod_{j\not=i} \frac{X-x_j}{x_i-x_j} f(X)=i=0n1yij=ixixjXxj

得到X处的值

Stern-Brocot树(求值大于up分子分母小于n的最简分数)

求分子分母小于n的且分数值大于up的最简分数

Frac SBtree(Frac up)
{
	Frac L=Frac(0,1),R=Frac(1,0);
	LL x=up.son,y=up.mon;
	Frac mid(1,1);
	Frac ans=Frac(1,1);
	while(x!=y&&mid.son<=n&&mid.mon<=n)
	{
//		cout<<mid.son<<' '<<mid.mon<<endl;
		if(mid.son*up.mon>mid.mon*up.son) ans=mid;
		if(x<y)
		{
			R=mid;
			mid=Frac(L.son+R.son,L.mon+R.mon);
			y-=x;
		}
		else
		{
			L=mid;
			mid=Frac(L.son+R.son,L.mon+R.mon);
			x-=y;
		}
	}
	return ans;
}
高斯消元
整数类型
int Gauss(int equ,int var)//equ:表示多少可变元,var: 表示多少列 
{
    int k,max_r,col = 0,ta,tb;
    int LCM,temp,num = 0,free_index;
    for(int i=0;i<=var;i++)
	{
        x[i]=0;
        free_x[i]=true;
    }
    for(k=0;k<equ && col<var;k++,col++)
	{
        max_r=k;
        for(int i=k+1;i<equ;i++)
            if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
        
        if(max_r!=k)// 与第k行交换.
            for(int j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]);
        if(a[k][col]==0)// 说明该col列第k行以下全是0了,则处理当前行的下一列
		{
            free_x[num++] = col;  k--;
            continue;
        }
        for(int i=k+1;i<equ;i++)
		{// 枚举要删去的行.
            if(a[i][col]!=0)
			{
                LCM=lcm(abs(a[i][col]),abs(a[k][col]));
                ta=LCM/abs(a[i][col]);
                tb=LCM/abs(a[k][col]);
                if(a[i][col]*a[k][col]<0) tb=-tb;//异号的情况是相加
                for(int j=col;j<var+1;j++) a[i][j]=a[i][j]*ta-a[k][j]*tb;
            }
        }
    }
    //无解
    for(int i=k; i<equ;i++) if(a[i][col]!=0) return -1;
    //无穷解
    if(k<var) return var-k; // 自由变元有var - k个.
    //唯一解
    for (int i = var - 1; i >= 0; i--)
	{
        temp = a[i][var];
        for(int j = i + 1; j<var; j++) if(a[i][j] != 0) temp-=a[i][j]*x[j];
        if(temp%a[i][i]!=0) return -2;      // 说明有浮点数解,但无整数解.
        x[i]=temp/a[i][i];
    }
    return 0;
}
b.浮点数类型(HihoCoder1195)
using namespace std;
typedef long long ll;

ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
double s[N][N],a[N][N],ans[N];
const double EPS=1e-10;
int Gauss_Elimination()
{
    int k=1,col=1;
    for(;col<=n&&k<=m;col++,k++)
    {
        int r=k;
        for(int i=k+1;i<=m;i++) if(fabs(a[i][col])>fabs(a[r][col])) r=i;
        if(fabs(a[r][col])<EPS) { k--;continue; }//自由元
        if(r!=k) swap(a[r],a[k]);
        for(int i=k+1;i<=m;i++)
        {
            if(fabs(a[i][col])>EPS)
            {
                double t=a[i][col]/a[k][col];
                for(int j=col;j<=n+1;j++) a[i][j]-=a[k][j]*t;
 //当前行为k,当前列为col,要化成上三角矩阵,所以这里将后面的式子中当前列全部变为0,
//做法就是将第k行化为第col列与当前行系数相同的式子,然后当前行依次减掉它。
            }
        }
    }
    for(int i=k;i<=m;i++)  if(fabs(a[i][n+1])>EPS) return -1;   //无解
    if(k<=n) return n-k+1;    //返回自由元个数(有多解)
    for(int i=n;i;i--)
    {
        for(int j=i+1;j<=n;j++) a[i][n+1]-=a[i][j]*a[j][n+1];
        a[i][n+1]/=a[i][i];
    }          //回带过程
    return 0;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++) 
    	for(int j=1;j<=n+1;j++) a[i][j]=read();
    int t=Gauss_Elimination();
    if(t<0) puts("No solutions");
    else if(t>0) puts("Many solutions");
    else for(int i=1;i<=n;i++) printf("%d\n",int(a[i][n+1]+0.5));
    return 0;
}
C.异或类型
(HihoCoder1196)
void Gauss_Elimination()
{
    int k=1,col=1;
    for(;k<=n&&col<=n;k++,col++)
    {
        int r=k;
        for(int i=k;i<=n;i++) if(a[i][col]==1) {
            r=i;break;
        }
        if(!a[r][col]) {k--;continue;}
        if(r!=k) swap(a[r],a[k]);
        for(int i=k+1;i<=n;i++)
        {
            if(!a[i][col]) continue;
            for(int j=col;j<=n+1;j++) a[i][j]^=a[k][j];
            //和解线性方程组有些不同,但都是化成上三角形式,想想发现其实就是将两个式子合并,将当前列化为0的新等式。
        }
    }
    for(int i=n;i;i--)
    {
        for(int j=i+1;j<=n;j++)  a[i][n+1]^=(a[j][n+1]*a[i][j]);//只有两个都为1才算。
        //这里并不需要管a[i][i],输出看看发现全是1.
    }
    int sum=0;
    for(int i=1;i<=n;i++) if(a[i][n+1]) sum++;
    printf("%d\n",sum);
    for(int i=1;i<=n;i++) if(a[i][n+1]) Get(i);
}
异或二:
int a[nmax][nmax];
int x[nmax];
int hashback[nmax][nmax];
int free_x[nmax];
char mp[nmax][nmax];
int ans1,ans2,equ,var;
int Gauss()
{
    int max_r;
    int col=0,num = 0;
    int k;
    for(int i = 0;i<=var;++i) x[i] = free_x[i] = 0;
    for(k = 0;k < equ && col < var;k++,col++)
    {
        max_r=k;
        for(int i=k+1;i<equ;i++)  if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
        if(max_r!=k)  for(int j=k ;j<var+1;j++) swap(a[k][j],a[max_r][j]);
        if(a[k][col]==0)
        {
            free_x[num++] = col;
            k--; continue;
        }
        for(int i=k+1;i<equ;i++)
        {
            if(a[i][col]!=0)
                for(int j=col;j<var+1;j++) a[i][j]^=a[k][j];
        }
    }
    for(int i = k;i<equ;++i)  if(a[i][col] != 0) return -1;
    if(k < var) return var - k;
    for(int i = var - 1; i >= 0; i--)
    {
        x[i]=a[i][var];
        for(int j = i + 1; j < var; j++) x[i] ^= ( a[i][j] && x[j]);
    }
    return 0;
}
矩阵求行列式

模数不为素数

int Gauss()
{
    int ans = 1;
    for(int i = 1; i < tot; i ++)
    {
        for(int j = i + 1; j < tot; j ++)
            while(f[j][i])
            {
                int t = f[i][i] / f[j][i];
                for(int k = i; k < tot; k ++)
                    f[i][k] = (f[i][k] - t * f[j][k] + mod) % mod;
                swap(f[i], f[j]);
                ans = - ans;
            }
        ans = (ans * f[i][i]) % mod;
    }
    return (ans + mod) % mod;
}

模数为素数

int gaussian(){
    int s=n-1,res=1;
    for(int i=1;i<=s;i++){
        if(!a[i][i]){
            int t;
            for(t=i+1;t<=s&&!a[t][i];t++);
            if(t>s) return 0;
            for(int j=i;j<=s;j++) swap(a[i][j],a[t][j]);
            res=-res;
        }
        int inv=ksm(a[i][i],MOD-2);
        for(int j=i+1;j<=s;j++){
            int t=mul(a[j][i],inv);
            for(int k=i;k<=s;k++)
                a[j][k]=plus(a[j][k],-mul(a[i][k],t));
        }
    }
    for(int i=1;i<=s;i++) res=mul(res,a[i][i]);
    return res;
}

组合

常用组合数公式

1.C(n,r)=C(n-1,r)+C(n-1,r-1)

2.C(n,0)+C(n+1,1)+C(n+2,2)+ …+C(n+r,r)=C(n+r+1,r)

**3.C(n,l)C(l,r)=C(n,r)C(n-r,l-r)

4.C(n,0)+C(n,1)+…+C(n,n-1)+C(n,n)=2n

5.C(n,0)-C(n,1)+C(n,2)-…±C(n,n)=0

**6.C(m+n,r)=C(m,0)*C(n,r)+C(m,1)C(n,r-1)+…+C(m,r)C(n,0)

**7.C(m+n,m)=C(m,0)*C(n,0)+C(m,1)C(n,1)+…+C(m,m)C(n,m)

8.C(r,r)+C(r+1,r)+C(r+2,r)….+C(n,r)=C(n+1,r+1)

Lucas与EXLucas

模数为素数时Lucas

ll pow(ll a, ll b, ll m)
{
    ll ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)ans = (ans % m) * (a % m) % m;
        b /= 2;
        a = (a % m) * (a % m) % m;
    }
    ans %= m;
    return ans;
}
ll inv(ll x, ll p)//x关于p的逆元,p为素数
{
    return pow(x, p - 2, p);
}
ll C(ll n, ll m, ll p)//组合数C(n, m) % p
{
    if(m > n)return 0;
    ll up = 1, down = 1;//分子分母;
    for(int i = n - m + 1; i <= n; i++)up = up * i % p;
    for(int i = 1; i <= m; i++)down = down * i % p;
    return up * inv(down, p) % p;
}
ll Lucas(ll n, ll m, ll p)
{
    if(m == 0)return 1;
    return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}

模数为非素数时EXLucas

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
ll pow(ll a, ll b, ll m)
{
    ll ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)ans = (ans % m) * (a % m) % m;
        b /= 2;
        a = (a % m) * (a % m) % m;
    }
    ans %= m;
    return ans;
}
ll extgcd(ll a, ll b, ll& x, ll& y)
//求解ax+by=gcd(a, b)
//返回值为gcd(a, b)
{
    ll d = a;
    if(b)
    {
        d = extgcd(b, a % b, y, x);
        y -= (a / b) * x;
    }
    else x = 1, y = 0;
    return d;
}
ll mod_inverse(ll a, ll m)
//求解a关于模上m的逆元
//返回-1表示逆元不存在
{
    ll x, y;
    ll d = extgcd(a, m, x, y);
    return d == 1 ? (m + x % m) % m : -1;
}

ll Mul(ll n, ll pi, ll pk)//计算n! mod pk的部分值  pk为pi的ki次方
//算出的答案不包括pi的幂的那一部分
{
    if(!n)return 1;
    ll ans = 1;
    if(n / pk)
    {
        for(ll i = 2; i <= pk; i++) //求出循环节乘积
            if(i % pi)ans = ans * i % pk;
        ans = pow(ans, n / pk, pk); //循环节次数为n / pk
    }
    for(ll i = 2; i <= n % pk; i++)
        if(i % pi)ans = ans * i % pk;
    return ans * Mul(n / pi, pi, pk) % pk;//递归求解
}

ll C(ll n, ll m, ll p, ll pi, ll pk)//计算组合数C(n, m) mod pk的值 pk为pi的ki次方
{
    if(m > n)return 0;
    ll a = Mul(n, pi, pk), b = Mul(m, pi, pk), c = Mul(n - m, pi, pk);
    ll k = 0, ans;//k为pi的幂值
    for(ll i = n; i; i /= pi)k += i / pi;
    for(ll i = m; i; i /= pi)k -= i / pi;
    for(ll i = n - m; i; i /= pi)k -= i / pi;
    ans = a * mod_inverse(b, pk) % pk * mod_inverse(c, pk) % pk * pow(pi, k, pk) % pk;//ans就是n! mod pk的值
    ans = ans * (p / pk) % p * mod_inverse(p / pk, pk) % p;//此时用剩余定理合并解
    return ans;
}

ll Lucas(ll n, ll m, ll p)
{
    ll x = p;
    ll ans = 0;
    for(ll i = 2; i <= p; i++)
    {
        if(x % i == 0)
        {
            ll pk = 1;
            while(x % i == 0)pk *= i, x /= i;
            ans = (ans + C(n, m, p, i, pk)) % p;
        }
    }
    return ans;
}

int main()
{
    ll n, m, p;
    while(cin >> n >> m >> p)
    {
        cout<<Lucas(n, m, p)<<endl;
    }
    return 0;
}
卡特兰数

Q:要求构造长度为n的序列 { a n } \{a_n\} {an}(令其和序列为 S n S_n Sn)使得有
{ ∀ i   S i ≥ 0 ∀ i a i = 0   o r   1   o r − 1 l   ≤ S n ≤ r \begin{cases}\forall_i \ S_i\ge 0\\\forall_i a_i=0\ or\ 1\ or -1\\l\ \le S_n\le r\end{cases} i Si0iai=0 or 1 or1l Snr
问可以构造多少这样的序列.由于最终的答案可能很大,因此对p取模

A:

考虑现在有i个0,则先将i个0分配到序列中,剩余未设定的数的数量设为m.则在着m个未设定数字中只有1与-1.对一确定的长度m,要让其所有的前缀和大于0,且有最终值介于lr之间,设最终达到的值为2x.设m=2k,则1有k+x,-1有k-x.只考虑最终的值为x则有组合数 C 2 k k + x C_{2k}^{k+x} C2kk+x。但是这并不是最终答案,我们还需要减去其中会小于0的情况.如果其中存在少于0的部分,则必然存在某个位置2h+1此时有h+1个-1,h个1,而剩下的序列也就会剩下 k + x − h k+x-h k+xh个1, k − x − h − 1 k-x-h-1 kxh1个-1,将-1与1数量翻转,则整个序列将存在 k + x + 1 k+x+1 k+x+1个-1. k − x − 1 k-x-1 kx1个1,则有少于0的前缀和的组合数即为 C 2 k k + x + 1 C_{2k}^{k+x+1} C2kk+x+1。则最终答案介于lr之间的组合数就是 ∑ x = l r ( C 2 k k + x − C 2 k k + x + 1 ) \sum_{x=l}^{r}(C_{2k}^{k+x}-C_{2k}^{k+x+1}) x=lr(C2kk+xC2kk+x+1)则最终的求和公式即为 C 2 k k + ( l + 1 ) / 2 − C 2 k k + r / 2 + 1 C_{2k}^{k+(l+1)/2}-C_{2k}^{k+r/2+1} C2kk+(l+1)/2C2kk+r/2+1以上的其实可以算作是卡特兰数的一个拓展.而在组合数的计算中,由于需要取模故需要求逆元,但是,并不能保证模数与阶乘互素,因此需要对阶乘提出所有与模数的公因数.将其额外提出运算,而剩余部分直接做逆元.

#include<bits/stdc++.h>
using namespace std;
#define int long long 
typedef long long LL;
const int size=1e5+5;
int n,p,l,r;
int tot;
int prime[45];
int fac[size],inv[size];
int tim[size][45];
LL quick_pow(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
int phi(int n)
{
	int res=n;
	for(int i=2;i*i<=n;i++)
	{
		if(n%i==0)
		{
			res=res-res/i;
			do n/=i;
			while(n%i==0);
		}
	}
	if(n>1)
	res=res-res/n;
	return res;
}				
void init()
{
	int temp=p;
	for(int i=2;i*i<=temp;i++)
	{
		if(temp%i==0)
		{
			prime[tot++]=i;
			do temp/=i;
			while(temp%i==0);
		}
	}
	if(temp>1) prime[tot++]=temp;
	fac[0]=fac[1]=1;
	inv[0]=inv[1]=1;
	int phip=phi(p);
	memset(tim,0,sizeof(tim));
	for(int i=2;i<=n;i++)
	{
		int x=i;
		for(int j=0;j<tot;j++)
		{
			tim[i][j]=tim[i-1][j];
			while(x%prime[j]==0) x/=prime[j],tim[i][j]++;
		}
		fac[i]=fac[i-1]*x%p;
		inv[i]=quick_pow(fac[i],phip-1);
	}
}
int combi(int n,int m)
{
	if(m>n) return 0;
	if(m<0) return 0;
	if(m==0) return 1;
	LL ans=(fac[n]*inv[n-m]%p)*inv[m]%p;
	for(int i=0;i<tot;i++)
	{
		ans=ans*quick_pow(prime[i],tim[n][i]-tim[n-m][i]-tim[m][i])%p;
	}
	return ans;
}
int32_t main()
{
	scanf("%lld%lld%lld%lld",&n,&p,&l,&r);
	LL ans=0;
	init();
	for(int i=0;i<=n;i++)
	{
		int m=n-i;
		ans=(ans+combi(n,i)*((-combi(m,(m+min(r,m))/2+1)+combi(m,(m+l+1)/2)+p)%p))%p;
	}
	printf("%lld\n",ans);
}
Cayley公式

T n , k T_{n,k} Tn,k为n个有标号点分成k个连通块的的分割种数,其中节点1,2,3,…k属于不同的联通块
T n , k = k   n n − k − 1 T_{n,k}=k\ n^{n-k-1} Tn,k=k nnk1

Prufer编码

所有的普吕弗序列和树唯一相对应,且普吕弗序列中点出现的次数为这个点在树中的度数-1,因此,在给出了一棵树的度数之后想要求得树的数量,就可以转化为,求序列的组合的数量,设无度数要求的点数是num,有要求的点数是poi,有要求的点在序列中出现的次数为sum,点的总数是n,那么答案就是:
a n s = A n − 2 s u m ∏ i = 1 p o i ( d u [ i ] − 1 ) ! ∗ n u m n − s u m − 2 ans= \frac {A^{sum}_{n-2}}{\prod_{i=1}^{poi}(du[i]-1)!}*num^{n-sum-2} ans=i=1poi(du[i]1)!An2sumnumnsum2

矩阵树定理

Q:对于给定的一幅图,要求从中选出一些边,使得构成最小生成树,问有多少种构造方法?

A:当i=j时, BBTij=vi的度数;而当i≠j时,如果存在边(vi,vj),那么BBTij=-1,否则BBTij=0。由此构建出图的Kirchhoff矩阵。则生成树的数量就是这个矩阵的任意n-1阶主子式行列式的值

其他

辗转相除法求两分数之间分子分母尽可能小的分数

Q:给出一个p和x要求求出最小的b满足
a ≡ b x ( m o d   p ) a\equiv bx(mod\ p) abx(mod p)

A:先对原式进行一些转化
a ≡ b x ( m o d   p ) a = b x − p c ( c ∈ Z ) ∵ 0 ≤ a < b → 0 ≤ b x − p c < b → p x ≤ b c < p x − 1 \begin{aligned} a&\equiv bx(mod\ p)\\ a&=bx-pc(c\in Z)\\ \because &0\le a<b\\ \rightarrow &0\le bx-pc<b\\ \rightarrow&\frac{p}{x}\le \frac{b}{c}<\frac{p}{x-1} \end{aligned} aabx(mod p)=bxpc(cZ)0a<b0bxpc<bxpcb<x1p
原问题就转化成了一个经典问题,求满足值在所给的两分数之间时,最小的分子分母是多少。

⌈ p x ⌉ = d \left\lceil\frac{p}{x}\right\rceil=d xp=d,当 ⌊ p x − 1 ⌋ ≥ d \left\lfloor\frac{p}{x-1}\right\rfloor\ge d x1pd时,显然有b=d,c=1为最优解,但是当不满足这个条件时,首先可以发现 p x \frac{p}{x} xp p x − 1 \frac{p}{x-1} x1p之间的差值必然小于1,由此
p x − ( d − 1 ) ≤ b c − ( d − 1 ) < p x − 1 − ( d − 1 ) \begin{aligned} \frac{p}{x}-(d-1)\le \frac{b}{c}-(d-1)<\frac{p}{x-1}-(d-1) \end{aligned} xp(d1)cb(d1)<x1p(d1)
此时可以发现不等式外侧的两个值都变成了真分数,只要对其作倒数就又变成了假分数则又可以进行上述操作,最终到达满足条件的答案再倒退回来就是答案

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL p,a;
//solve "pa/pb<x/y<=qa/qb" 's min(y),min(x)
void solve(LL pa,LL pb,LL qa,LL qb,LL &x,LL &y)
{
	LL z=(pa+pb-1)/pb;
	if(z<=qa/qb){
		x=z;y=1;
		return ;
	}
	pa-=(z-1)*pb;qa-=(z-1)*qb;
	solve(qb,qa,pb,pa,y,x);
	x+=(z-1)*y;
}
int main()
{
	int t;
	scanf("%d",&t);
	LL x,y;
	while(t--)
	{
		scanf("%lld%lld",&p,&a);
		solve(p,a,p,a-1,x,y);
		printf("%lld/%lld\n",x*a-p*y,x);
	}
}
蔡勒公式

给出年月日求是星期几?(只适用于1582.10.15以后)

int zeller(int y,int m,int d) {
	if (m<=2) y--,m+=12;
	int c=y/100;
	y%=100;
	int w=((c>>2)-(c<<1)+y+(y>>2)+(13*(m+1)/5)+d-1)%7;
	if (w<0) w+=7;
	return(w);
}
BM与广义BM

BM

(解线性递推式)
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1) { if(b&1)res=res*a%mod; a=a*a%mod;  }  return res;  }
ll _,n;
namespace linear_seq {
    const int N=10010;
    ll res[N],base[N],_c[N],_md[N];
    vector<ll> Md;
    void mul(ll *a,ll *b,int k) 
	{
        rep(i,0,k+k) _c[i]=0;
        rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]= (_c[i+j]+a[i]*b[j])%mod;
        for (int i=k+k-1;i>=k;i--)  if (_c[i])
            rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
        rep(i,0,k) a[i]=_c[i];
    }
    int solve(ll n,VI a,VI b) 
	{
        ll ans=0,pnt=0;
        int k=SZ(a);
        assert(SZ(a)==SZ(b));
        rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
        Md.clear();
        rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
        rep(i,0,k) res[i]=base[i]=0;
        res[0]=1;
        while ((1ll<<pnt)<=n) pnt++;
        for (int p=pnt;p>=0;p--) 
		{
            mul(res,res,k);
            if ((n>>p)&1) 
			{
                for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
            }
        }
        rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1,1),B(1,1);
        int L=0,m=1,b=1;
        rep(n,0,SZ(s)) {
            ll d=0;
            rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
            if (d==0) ++m;
            else if (2*L<=n) {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            } else {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }
    int gao(VI a,ll n) {
        VI c=BM(a);
        c.erase(c.begin());
        rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
        return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};
int T;
int main() 
{
	scanf("%d",&T);
    while(T--) 
	{
		scanf("%lld",&n);
        vector<int>v;
        //关系式前几项,一般10项 
        v.push_back(a[i]);
        v.push_back(a[2]);
        //........
 		v.push_back(a[x]);
        printf("%lld\n",linear_seq::gao(v,n-1)%mod);
    }
}

广义BM

求线性递推式的最短通项

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9;
struct LinearRecurrence {
    using vec = std::vector<LL>;
    static void extand(vec &a, size_t d, LL value = 0) {
        if (d <= a.size()) return;
        a.resize(d, value);
    }
    static void exgcd(LL a, LL b, LL &g, LL &x, LL &y) {
        if (!b) x = 1, y = 0, g = a;
        else {
            exgcd(b, a % b, g, y, x);
            y -= x * (a / b);
        }
    }
    static LL crt(const vec &c, const vec &m) {
        int n = c.size();
        LL M = 1, ans = 0;
        for (int i = 0; i < n; ++i) M *= m[i];
        for (int i = 0; i < n; ++i) {
            LL x, y, g, tm = M / m[i];
            exgcd(tm, m[i], g, x, y);
            ans = (ans + tm * x * c[i] % M) % M;
        }
        return (ans + M) % M;
    }
    static vec ReedsSloane(const vec &s, LL mod) {
        auto inverse = [](LL a, LL m) {
            LL d, x, y;
            exgcd(a, m, d, x, y);
            return d == 1 ? (x % m + m) % m : -1;
        };
        auto L = [](const vec &a, const vec &b) {
            int da = (a.size() > 1 || (a.size() == 1 && a[0])) ? a.size() - 1 : -1000;
            int db = (b.size() > 1 || (b.size() == 1 && b[0])) ? b.size() - 1 : -1000;
            return std::max(da, db + 1);
        };
        auto prime_power = [&](const vec &s, LL mod, LL p, LL e) {
            // linear feedback shift register mod p^e, p is prime
            std::vector<vec> a(e), b(e), an(e), bn(e), ao(e), bo(e);
            vec t(e), u(e), r(e), to(e, 1), uo(e), pw(e + 1);
            pw[0] = 1;
            for (int i = pw[0] = 1; i <= e; ++i) pw[i] = pw[i - 1] * p;
            for (LL i = 0; i < e; ++i) {
                a[i] = {pw[i]}, an[i] = {pw[i]};
                b[i] = {0}, bn[i] = {s[0] * pw[i] % mod};
                t[i] = s[0] * pw[i] % mod;
                if (t[i] == 0)
                    t[i] = 1, u[i] = e;
                else
                    for (u[i] = 0; t[i] % p == 0; t[i] /= p, ++u[i]);
            }
            for (size_t k = 1; k < s.size(); ++k) {
                for (int g = 0; g < e; ++g)
                    if (L(an[g], bn[g]) > L(a[g], b[g])) {
                        ao[g] = a[e - 1 - u[g]];
                        bo[g] = b[e - 1 - u[g]];
                        to[g] = t[e - 1 - u[g]];
                        uo[g] = u[e - 1 - u[g]];
                        r[g] = k - 1;
                    }
                a = an, b = bn;
                for (int o = 0; o < e; ++o) {
                    LL d = 0;
                    for (size_t i = 0; i < a[o].size() && i <= k; ++i) {
                        d = (d + a[o][i] * s[k - i]) % mod;
                    }
                    if (d == 0) {
                        t[o] = 1, u[o] = e;
                    } else {
                        for (u[o] = 0, t[o] = d; t[o] % p == 0; t[o] /= p, ++u[o]);
                        int g = e - 1 - u[o];
                        if (L(a[g], b[g]) == 0) {
                            extand(bn[o], k + 1);
                            bn[o][k] = (bn[o][k] + d) % mod;
                        } else {
                            LL coef =t[o] * inverse(to[g], mod) % mod * pw[u[o] - uo[g]] % mod;
                            int m = k - r[g];
                            extand(an[o], ao[g].size() + m);
                            extand(bn[o], bo[g].size() + m);
                            for (size_t i = 0; i < ao[g].size(); ++i) {
                                an[o][i + m] -= coef * ao[g][i] % mod;
                                if (an[o][i + m] < 0) an[o][i + m] += mod;
                            }
                            while (an[o].size() && an[o].back() == 0) an[o].pop_back();
                            for (size_t i = 0; i < bo[g].size(); ++i) {
                                bn[o][i + m] -= coef * bo[g][i] % mod;
                                if (bn[o][i + m] < 0) bn[o][i + m] -= mod;
                            }
                            while (bn[o].size() && bn[o].back() == 0) bn[o].pop_back();
                        }
                    }
                }
            }
            return std::make_pair(an[0], bn[0]);
        };
        std::vector<std::tuple<LL, LL, int>> fac;
        for (LL i = 2; i * i <= mod; ++i)
            if (mod % i == 0) {
                LL cnt = 0, pw = 1;
                while (mod % i == 0) mod /= i, ++cnt, pw *= i;
                fac.emplace_back(pw, i, cnt);
            }
        if (mod > 1) fac.emplace_back(mod, mod, 1);
        std::vector<vec> as;
        size_t n = 0;
        for (auto &&x: fac) {
            LL mod, p, e;
            vec a, b;
            std::tie(mod, p, e) = x;
            auto ss = s;
            for (auto &&x: ss) x %= mod;
            std::tie(a, b) = prime_power(ss, mod, p, e);
            as.emplace_back(a);
            n = std::max(n, a.size());
        }
        vec a(n), c(as.size()), m(as.size());
        for (size_t i = 0; i < n; ++i) {
            for (size_t j = 0; j < as.size(); ++j) {
                m[j] = std::get<0>(fac[j]);
                c[j] = i < as[j].size() ? as[j][i] : 0;
            }
            a[i] = crt(c, m);
        }
        return a;
    }
    LinearRecurrence(const vec &s, const vec &c, LL mod) :
            init(s), trans(c), mod(mod), m(s.size()) {}
  
    LinearRecurrence(const vec &s, LL mod, bool is_prime = true) : mod(mod) {
        vec A;
        A = ReedsSloane(s, mod);
        if (A.empty()) A = {0};
        m = A.size() - 1;
        trans.resize(m);
        for (int i = 0; i < m; ++i) trans[i] = (mod - A[i + 1]) % mod;
          
        std::reverse(trans.begin(), trans.end());
        init = {s.begin(), s.begin() + m};
    }
   
    LL calc(LL n) {
        if (mod == 1) return 0;
        if (n < m) return init[n];
        vec v(m), u(m << 1);
        int msk = !!n;
        for (LL m = n; m > 1; m >>= 1) msk <<= 1;
        v[0] = 1 % mod;
        for (int x = 0; msk; msk >>= 1, x <<= 1) {
            std::fill_n(u.begin(), m * 2, 0);
            x |= !!(n & msk);
            if (x < m) u[x] = 1 % mod;
            else {// can be optimized by fft/ntt
                for (int i = 0; i < m; ++i)
                    for (int j = 0, t = i + (x & 1); j < m; ++j, ++t)
                        u[t] = (u[t] + v[i] * v[j]) % mod;
                for (int i = m * 2 - 1; i >= m; --i)
                    for (int j = 0, t = i - m; j < m; ++j, ++t)
                        u[t] = (u[t] + trans[j] * u[i]) % mod;
            }
            v = {u.begin(), u.begin() + m};
        }
        LL ret = 0;
        for (int i = 0; i < m; ++i) ret = (ret + v[i] * init[i]) % mod;
        return ret;
    }
    vec init, trans;
    LL mod;
    int m;
};
int quick_pow(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=1LL*a*ans%mod;
        a=1LL*a*a%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    vector<LL> f;
    f.push_back(0),f.push_back(1);
    for(int i=2;i<=2000;i++)
    {
        f.push_back((f[i-1]+f[i-2])%mod);
    }
    for(int i=1;i<=2000;i++) f[i]=quick_pow(f[i],m);
    for(int i=1;i<=2000;i++) f[i]=(f[i]+f[i-1])%mod;
    LinearRecurrence qw(f,mod,false);
    printf("%lld\n",qw.calc(n));
}

单纯形

用于求解满足不等式组 A X ≥ B AX\ge B AXB m i n ( C X ) min(CX) min(CX)

当求满足条件的最大的CX时,须对A矩阵进行转置,并对BC进行转置并交换

namespace LP{
	const int INF=0x3f3f3f3f;
	const int N=505;
	const int M=30000;
	const double eps=1e-6;
	int n,m;
	double a[N][M],b[N],c[M],v;
	void init(int _n,int _m){n=_n,m=_m;v=0;}
	void builda(int i,int j,int val){a[i][j]=val;}
	void buildb(int i,int val){b[i]=val;}
	void buildc(int i,int val){c[i]=val;}
	inline void pivot(int l,int e)//矩阵的转秩
	{
	    b[l]/=a[l][e];
	    for(int j=1;j<=n;++j)
	    {
	        if(j!=e) a[l][j]/=a[l][e];      
	    }
	    a[l][e]=1/a[l][e];
	    for(int i=1;i<=m;++i)
	    {
	        if(i!=l&&fabs(a[i][e])>0)
	        {
	            b[i]-=a[i][e]*b[l];
	            for(int j=1;j<=n;++j)
	            {
	                if(j!=e) a[i][j]-=a[i][e]*a[l][j];
	            }
	            a[i][e]=-a[i][e]*a[l][e];
	        }
	    }
	    v+=c[e]*b[l];
	    for(int j=1;j<=n;++j)
	    {
	        if(j!=e) c[j]-=c[e]*a[l][j];
	    }
	    c[e]=-c[e]*a[l][e];
	}
	 
	inline double simplex()
	{
	    while(1)
	    {
	        int e=0,l=0;
	        for(e=1;e<=n;++e)
	        {
	            if(c[e]>eps) break;
	        }
	        if(e==n+1) return v;
	        double mn=INF;
	        for(int i=1;i<=m;++i)
	        {
	            if(a[i][e]>eps&&mn>b[i]/a[i][e]) mn=b[i]/a[i][e],l=i;
	        }
	        if(mn==INF) return INF;
	        pivot(l,e);
	    }
	}
}
常用的前缀和公式

∑ i = 1 n = ( 1 + n ) n 2 \sum_{i=1}^n=\frac{(1+n)n}{2} i=1n=2(1+n)n
2.
∑ i = 1 n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum_{i=1}^ni^2=\frac{n(n+1)(2n+1)}{6} i=1ni2=6n(n+1)(2n+1)
3.
∑ i = 1 n i 3 = ( n ( n + 1 ) 2 ) 2 \sum_{i=1}^ni^3=(\frac{n(n+1)}{2})^2 i=1ni3=(2n(n+1))2

图论

网络流

最大流
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=2005;
const int maxm=5e6+5;
const LL inf=1e12;
struct Edge{
    int to,nxt;
	LL cap,flow;
}edge[maxm];
int tol;
int head[maxn];
inline void init(){
    tol=2;
    memset(head,-1,sizeof(head));
}
inline void AddEdge(int u,int v,LL w,int rw=0){
    edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;
    edge[tol].nxt=head[u];head[u]=tol++;
    edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;
    edge[tol].nxt=head[v];head[v]=tol++;
}
int Q[maxn];
int dep[maxn],cur[maxn],sta[maxn];
inline bool bfs(int s,int t,int n){
    int front=0,tail=0;
    memset(dep,-1,sizeof(dep[0])*(n+1));
    dep[s]=0;
    Q[tail++]=s;
    while(front<tail){
        int u=Q[front++];
        for(register int i=head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(edge[i].cap>edge[i].flow&&dep[v]==-1){
                dep[v]=dep[u]+1;
                if(v==t) return true;
                Q[tail++]=v;
            }
        }
    }
    return false;
}
inline LL dinic(int s,int t,int n){
    LL maxflow=0;
    while(bfs(s,t,n)){
        for(register int i=0;i<n;++i) cur[i]=head[i];
        int u=s,tail=0;
        while(cur[s]!=-1){
            if(u==t){
                LL tp=inf;
                for(register int i=tail-1;i>=0;--i)
                {
                    tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
                }
                maxflow+=tp;
                for(register int i=tail-1;i>=0;--i){
                    edge[sta[i]].flow+=tp;
                    edge[sta[i]^1].flow-=tp;
                    if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
                }
                u=edge[sta[tail]^1].to;
            }
            else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
                sta[tail++]=cur[u];
                u=edge[cur[u]].to;
            }
            else{
                while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
                cur[u] = edge [cur[u]].nxt;
            }
        }
    }
    return maxflow;
}
费用流
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=4005;
const int maxm=5e4+4;
const LL inf=1LL<<60;
const LL infc=2e11;
struct Edge{
	int to,next,flow,cost;
	LL cap;
}edge[maxm];
int head[maxn],tol;
int pre[maxn];LL dis[maxn];
bool vis[maxn];
int N;
void init(int n)
{
	N=n;
	tol=0;
	memset(head,-1,sizeof(head));
}
void AddEdge(int u,int v,LL cap,int cost){
	edge[tol].to=v;
	edge[tol].cap=cap;
	edge[tol].cost=cost;
	edge[tol].flow=0;
	edge[tol].next=head[u];
	head[u]=tol++;
	edge[tol].to=u;
	edge[tol].cap=0;
	edge[tol].cost=-cost;
	edge[tol].flow=0;
	edge[tol].next=head[v];
	head[v]=tol++;
}
bool spfa(int s,int t)
{
	queue<int> q;
	for(int i=0;i<=N;i++)
	{
		dis[i]=inf;
		vis[i]=false;
		pre[i]=-1;
	}
	dis[s]=0;
	vis[s]=true;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].next){
			int v=edge[i].to;
			if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost)
			{
				dis[v]=dis[u]+edge[i].cost;
				pre[v]=i;
				if(!vis[v]){
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	if(pre[t]==-1) return false;
	else return true;
}
int MincostMaxflow(int s,int t,LL &cost){
	LL flow=0;
	cost=0;
	while(spfa(s,t)){
		LL Min=inf;
		for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
			if(Min>edge[i].cap-edge[i].flow)
				Min=edge[i].cap-edge[i].flow;
		}
		for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
		edge[i].flow+=Min;
		edge[i^1].flow-=Min;
		cost+=1LL*edge[i].cost*Min;
		}
		flow+=Min;
	}
	return flow;
}
zkw费用流
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <iostream>
#include <algorithm>

using namespace std;

int n, m, S, T, slk[1001], dist[1001], first[1001], l, c[1000001], next[1000001], where[1000001], ll[1000001], v[1000001];
bool b[1001];
long long ans1, ans2;

inline void makelist(int x, int y, int z, int p){
    where[++l] = y; 
    ll[l] = z;
    v[l] = p;
    next[l] = first[x];
    first[x] = l;
}

inline void spfa(){    
    memset(dist, 127, sizeof(dist));
    memset(b, false, sizeof(b));
    dist[T] = 0; c[1] = T;
    for (int k = 1, l = 1; l <= k; l++)
    {
        int m = c[l];
        b[m] = false;
        for (int x = first[m]; x; x = next[x])
            if (ll[x ^ 1] && dist[m] - v[x] < dist[where[x]])
            {
               dist[where[x]] = dist[m] - v[x];
               if (!b[where[x]]) b[where[x]] = true, c[++k] = where[x];
            }
    }
}

int zkw_work(int now, int cap){
    b[now] = true;
    if (now == T)
    {
        ans1 += cap;
        ans2 += (long long)cap * dist[S];
        return(cap);
    }
    int Left = cap;
    for (int x = first[now]; x; x = next[x])
        if (ll[x] && !b[where[x]]) 
           if (dist[now] == dist[where[x]] + v[x])
           {
               int use = zkw_work(where[x], min(Left, ll[x]));
               ll[x] -= use; ll[x ^ 1] += use;
               Left -= use;
               if (!Left) return(cap);
           }
           else slk[where[x]] = min(slk[where[x]], dist[where[x]] + v[x] - dist[now]);
    return(cap - Left);
}

bool relax(){
    int Min = 1 << 30;
    for (int i = 0; i <= T; i++) 
        if (!b[i]) Min = min(Min, slk[i]);
    if (Min == 1 << 30) return(false);
    for (int i = 0; i <= T; i++)
        if (b[i]) dist[i] += Min;
    return(true);
}

inline void zkw(){
    ans1 = ans2 = 0;
    spfa();   //hint memset(dist, 0, sizeof(dist)); if all values of edges are nonnegative
    for (;;)
    {
        memset(slk, 127, sizeof(slk));
        for (;;)
        {
            memset(b, false, sizeof(b));
            if (!zkw_work(S, 1 << 30)) break;
        }
        if (!relax()) break;
    }
    printf("%I64d %I64d\n", ans1, ans2);
}

int main(){
    scanf("%d%d", &n, &m);
    S = 1; T = n;
    memset(first, 0, sizeof(first)); l = 1;
    for (int i = 1; i <= m; i++)
    {
        int x, y, z, q;
        scanf("%d%d%d%d", &x, &y, &z, &q);
        makelist(x, y, z, q); makelist(y, x, 0, -q);
    }
    zkw();
}
Dijkstra优化费用流
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e4;
const int inf = 0x3f3f3f3f;
struct edge {
    int to, cap, cost, rev;
    edge() {}
    edge(int to, int _cap, int _cost, int _rev) :to(to), cap(_cap), cost(_cost), rev(_rev) {}
};
int V, H[maxn + 5], dis[maxn + 5], PreV[maxn + 5], PreE[maxn + 5];
vector<edge> G[maxn + 5];
void init(int n) {
    V = n;
    for (int i = 0; i <= V; ++i)G[i].clear();
}
void AddEdge(int from, int to, int cap, int cost) {
    G[from].push_back(edge(to, cap, cost, G[to].size()));
    G[to].push_back(edge(from, 0, -cost, G[from].size() - 1));
}
int Min_cost_max_flow(int s, int t, int f, int& flow) {
    int res = 0; fill(H, H + 1 + V, 0);
    while (f) {
        priority_queue <pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > q;
        fill(dis, dis + 1 + V, inf);
        dis[s] = 0; q.push(pair<int, int>(0, s));
        while (!q.empty()) {
            pair<int, int> now = q.top(); q.pop();
            int v = now.second;
            if (dis[v] < now.first)continue;
            for (int i = 0; i < G[v].size(); ++i) {
                edge& e = G[v][i];
                if (e.cap > 0 && dis[e.to] > dis[v] + e.cost + H[v] - H[e.to]) {
                    dis[e.to] = dis[v] + e.cost + H[v] - H[e.to];
                    PreV[e.to] = v;
                    PreE[e.to] = i;
                    q.push(pair<int, int>(dis[e.to], e.to));
                }
            }
        }
        if (dis[t] == inf)break;
        for (int i = 0; i <= V; ++i)H[i] += dis[i];
        int d = f;
        for (int v = t; v != s; v = PreV[v])d = min(d, G[PreV[v]][PreE[v]].cap);
        f -= d; flow += d; res += d*H[t];
        for (int v = t; v != s; v = PreV[v]) {
            edge& e = G[PreV[v]][PreE[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}
int a[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(register int i=1;i<=n;++i) scanf("%d",&a[i]);
        int ss=0,s=1,t=2*n+2,tt=2*n+3;
        init(tt+1);
        AddEdge(ss,s,k,0);
        AddEdge(t,tt,k,0);
        for(register int i=1;i<=n;++i)
        {
            AddEdge(s,i+1,1,0);
            AddEdge(i+1+n,t,1,0);
            AddEdge(i+1,i+1+n,1,-a[i]);
            for(register int j=i+1;j<=n;++j)
            {
                if(a[j]>=a[i])
                {
                    AddEdge(1+i+n,1+j,1,0);
                }
            }
        }
        int ans=0;
        printf("%d\n",-Min_cost_max_flow(ss,tt,inf,ans));
    }
    return 0;
}

重要建图套路
  1. Q:给出一幅图,对其中的点进行黑白染色,边根据两个端点的颜色不同有不同的权值,设为A,B,C,问如何染色使得总的边权最大

    A:设一个源点一个汇点,其中xy都是原图的点,建下述图,通过作割表示不选择其他两种方案

    这样就要求有
    a + b = B + A c + d = C + B a + e + d = A + C b + e + c = A + C \begin{aligned} a+b&=B+A\\ c+d&=C+B\\ a+e+d&=A+C\\ b+e+c&=A+C \end{aligned} a+bc+da+e+db+e+c=B+A=C+B=A+C=A+C

在这里插入图片描述

可以得到一组解 a = b = ( A + B ) / 2 , c = d = ( C + B ) / 2 , e = − B + ( A + C ) / 2 a=b=(A+B)/2,c=d=(C+B)/2,e=-B+(A+C)/2 a=b=(A+B)/2,c=d=(C+B)/2,e=B+(A+C)/2

用所有的贡献减去这个的最小割就是答案

  1. Q:求二分图的最大独立集

    A: 对于二分图(U, V) 源连向U,V连向汇。跑完网络流后,残余网络上,从源可达的U中的点,和从源不可达的V中的点,加起来就是最大独立集了

  2. Q:给出一幅图,对其中的点进行黑白染色,染不同的颜色有不同的收益,其中如果有一些点(称为一个组)染相同的颜色就会额外获得一些收益,问收益最大

    A:设立一个源点一个汇点,源点向所有的点连接容量为白色时的收益,所有的点向汇点连接容量为黑色时的收益,对所有的组的不同颜色设立虚点,源点向全组为白色时的组的虚点连接组为白色时的收益,全组为黑色时的组的虚点向汇点连边。最终,总的收益减去这幅图的最小割就是答案

  3. Q:给出一幅图,请割出其字典序最小的最小割

    A: 对所有可能被割的边对字典序从小到大bfs作连通性检查,如果不联通则是最小割边集中的边,接下来对这条边向终点的跑一边dinic退流,这条边向源点跑一遍dinic退流,最后把这条边和其反向边容量置0,则这条边就会被从原本图中删去,接下来继续检测字典序更大的边即可,代码实现见后面的“字典序最小割”

  4. Q:给出一幅图,求最大权闭合子图

    A:将原图中的所有边照原状连接,容量无穷大,从源点向所有权值为正的点连边,所有权值为负的点向汇点连接边,容量均为点的权值的绝对值。最大权闭合子图的最大权值就是所有正权点的权值和减去这幅图的最小割

  5. Q:给出一幅图,需要满足所有的边的容量介于上下界之间,求可行流

    A:对于一副循环图(即无源点和汇点,起始点和终末点之间存在一条容量无限大的边)来说,假设其中的每条边的流量都介于[l,r]直接,我们不妨将l取出,将原边变为[0,r-l],与一条容量为l的必行边,并在这条边中间插入两个点,设为x,y,细节见图示

    img

    我们不难发现从x到y的流量只有x->y一条,因此其x->y的最大流也就是3,于是我们可以据此推理出假使存在一条从y到x的最大流等于3时就是所有的必行边都被走过了,那么这个图就是可行的。

    但是本题是一个有汇源点的图,所以我们要想办法将之转化为循环图,具体方法就是在汇点和源点直接也连上一条无穷大的边即可。需要注意的是:

    从汇点向源点连!

    最短路

    Dijkstra

    //Dijkstra
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<cstdio>
    #include<vector>
    #include<stack>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    #define int long long
    #define endl '\n'
    #define sc(x) scanf("%lld",&x)
     
    using namespace std;
    const int size=105;
    const int inf=0x3f3f3f3f;
    struct Edge{
    	int u,v,w;
    	Edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){} 
    }; 
    struct node{
    	int id,w;
    	node(int id=0,int w=0):id(id),w(w){}
    	friend bool operator<(node a,node b)
    	{
    		return a.w>b.w;
    	} 
    };
    priority_queue<node> q;
    vector<Edge> edge[size];
    int dis[size],vis[size];
    void Dijkstra(int beg)
    {
    	memset(dis,inf,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	while(!q.empty()) q.pop();
    	q.push(node(beg,0)),dis[beg]=0;
    	while(!q.empty())
    	{
    		node s=q.top();
    		q.pop();
    		int id=s.id;
    		if(dis[id]!=s.w) continue;
    		for(int i=0;i<edge[id].size();i++)
    		{
    			if(dis[edge[id][i].v]>dis[id]+edge[id][i].w)
    			{
    				dis[edge[id][i].v]=dis[id]+edge[id][i].w;
    				q.push(node(edge[id][i].v,dis[edge[id][i].v]));
    			}
    		}
    	}
    }
    int32_t main()
    {
    	int n,m;
    	while(cin>>n>>m&&n)
    	{
    		int i,u,v,w;
    		for(i=1;i<=n;i++) edge[i].clear(); 
    		for(i=0;i<m;i++)
    		{
    			scanf("%lld%lld%lld",&u,&v,&w);
    			edge[u].push_back(Edge(u,v,w));
    			edge[v].push_back(Edge(v,u,w));
    		}
    		Dijkstra(1);
    		cout<<dis[n]<<endl;
    	}
    	return 0;
    }
    

SPFA

//SPFA算法
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define int long long
#define endl '\n'
#define sc(x) scanf("%lld",&x)
 
using namespace std;
const int inf=0x3f3f3f3f;
const int size=1e4+5;
struct Edge{
	int u,v,w;
	Edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
};
vector<Edge> edge;
vector<int> No[105];
int vis[105];
int dis[105];
int cnt[105];
void init(int n)
{
	memset(dis,inf,sizeof(dis));
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++) No[i].clear();
	edge.clear();
}
void addedge(int u,int v,int w)
{
	edge.push_back(Edge(u,v,w));
	No[u].push_back(edge.size()-1);
}
int spfa(int s,int n)
{
	dis[s]=0;
	queue<int> Q;
	Q.push(s);
	while(!Q.empty())
	{
		int k=Q.front();
		Q.pop();
		vis[k]=0;
		for(int i=0;i<No[k].size();i++)
		{
			int id=No[k][i];
			if(dis[edge[id].v]>dis[k]+edge[id].w)
			{
				dis[edge[id].v]=dis[k]+edge[id].w;
				if(!vis[edge[id].v])
				{
					Q.push(edge[id].v);vis[edge[id].v]=1;
					if(++cnt[edge[id].v]>n) return 0;
				}
			}
		}
	}
	return 1;
}
int32_t main()
{
	int n,m;
	while(~scanf("%lld%lld",&n,&m)&&n)
	{
		int i;
		init(n);
		for(i=0;i<m;i++)
		{
			int u,v,w;
			scanf("%lld%lld%lld",&u,&v,&w);
			addedge(u,v,w);
			addedge(v,u,w);
		}
		if(spfa(1,n)) cout<<dis[n]<<endl;
		else printf("cant\n");
	}
	return 0;
}

Floyd

void Floyd(int n)
{
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);	
			}
		}
	}
}

DP

斜率DP

对于可以写成y=kx+b的DP式,其中k为只与i有关的已知数,y,x为只与j有关的变量,b为dp[i]与一常数的线性变换,则可用上凸包或者下凸包维护x,y得到最大或者最小的b,以优化转移。

luogu3195

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int size=5e4+5;
typedef long long LL;
int n,l;
int C;
int c[size];
int sumc[size];
int dp[size];
int q[2*size];
inline LL getans(int i,int j)
{
	return dp[j]+sumc[j]*sumc[j]-2*(sumc[i]-C)*sumc[j]+(sumc[i]-C)*(sumc[i]-C);
}
bool compare(int i,int j,int k)
{
	__int128 x1,y1,x2,y2,x3,y3;
	x1=sumc[i],y1=dp[i]+sumc[i]*sumc[i];
	x2=sumc[j],y2=dp[j]+sumc[j]*sumc[j];
	x3=sumc[k],y3=dp[k]+sumc[k]*sumc[k];
	return (y3-y2)*(x2-x1)<=(y2-y1)*(x3-x2);
}
int32_t main()
{
	scanf("%lld%lld",&n,&l);
	C=l+1;
	sumc[0]=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&c[i]);
		sumc[i]=sumc[i-1]+c[i];
	}
	for(int i=1;i<=n;i++) sumc[i]=sumc[i]+i;
	int tail=1,head=1;
	q[tail++]=dp[0]=0;
	for(int i=1;i<=n;i++)
	{
		while(head+1<tail&&getans(i,q[head])>=getans(i,q[head+1])) head++;
		dp[i]=getans(i,q[head]);
		while(head+1<tail&&compare(q[tail-2],q[tail-1],i)) tail--;
		q[tail++]=i;
	}
	printf("%lld\n",dp[n]);		
}

树型DP

Q:给出一颗树,有每个节点有两个权值,现在有两个人,这两个人走过同一个顶点有不同的收益,这两个人先后选择下一个要走的顶点,两个人都会走使得最终自己收益减去对方收益尽可能大的走法,问最终两个人的差值会有多大

A:对每个顶点的两个权值相减转换成一个权值之后,两个人的目的就变成了使得最终的收益尽可能大和尽可能小。对次可以得到转移式子
d p [ u ] [ 0 ] = a [ u ] + m i n ( d p [ v ] [ 1 ] ) d p [ u ] [ 1 ] = a [ u ] + m a x ( d p [ v ] [ 0 ] ) dp[u][0]=a[u]+min(dp[v][1])\\ dp[u][1]=a[u]+max(dp[v][0]) dp[u][0]=a[u]+min(dp[v][1])dp[u][1]=a[u]+max(dp[v][0])

#include<bits/stdc++.h>
#define int long long  
using namespace std;
typedef long long LL;
const int size=1e5+5;
//int head[size],tot,nxt[size],to[size];
vector<int> G[size];
const LL inf=1e15;
LL dpdown[size][2][2];
//dpdown 中的第三个参数指为0时为最大(小)1时为次大(小)
int a[size],tp;
// 0 zhang's choose, 1 another choose
// 1 is greater
LL dp[size][2];
int d[size];

inline void dfs1(int u,int p)
{
    for(auto v:G[u])
    {
        if(v==p) continue;
        dfs1(v,u);d[u]++;
        if(dpdown[u][0][0]>=dpdown[v][1][0]+a[u]) dpdown[u][0][1]=dpdown[u][0][0],dpdown[u][0][0]=dpdown[v][1][0]+a[u];
        else if(dpdown[u][0][1]>dpdown[v][1][0]+a[u]) dpdown[u][0][1]=dpdown[v][1][0]+a[u];
        if(dpdown[u][1][0]<=dpdown[v][0][0]+a[u]) dpdown[u][1][1]=dpdown[u][1][0],dpdown[u][1][0]=dpdown[v][0][0]+a[u];
        else if(dpdown[u][1][1]<dpdown[v][0][0]+a[u]) dpdown[u][1][1]=dpdown[v][0][0]+a[u];
    } 
    if(!d[u]) for(int i=0;i<=1;i++) for(int j=0;j<=1;j++) dpdown[u][i][j]=a[u];
}
//dfs2求出点u从除了自身子树外转移而来的dp值
void dfs2(int u,int p)
{
    for(auto v:G[u])
    {
        if(v==p) continue;
        if(d[u]==1){dp[v][0]=dp[u][1]+a[v],dp[v][1]=dp[u][0]+a[v];}
        else
        {
            if(a[u]+dpdown[v][0][0]==dpdown[u][1][0]) dp[v][0]=a[v]+dpdown[u][1][1];
            else dp[v][0]=a[v]+dpdown[u][1][0];
            if(u!=1) dp[v][0]=max(dp[v][0],a[v]+dp[u][1]);
            if(a[u]+dpdown[v][1][0]==dpdown[u][0][0]) dp[v][1]=a[v]+dpdown[u][0][1];
            else dp[v][1]=a[v]+dpdown[u][0][0];
            if(u!=1) dp[v][1]=min(dp[v][1],a[v]+dp[u][0]);
        }
        dfs2(v,u);
    }
}        
int n;    
int32_t main()
{
    int t;
    //freopen("b.in","r",stdin);
    scanf("%lld",&t);
    while(t--)
    {
        memset(d,0,sizeof(d));
        scanf("%lld",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++) scanf("%lld",&tp),a[i]-=tp;
        for(int i=1;i<=n;i++) G[i].clear(),dpdown[i][0][0]=dpdown[i][0][1]=inf,dpdown[i][1][0]=dpdown[i][1][1]=-inf;
        int x,y;
        for(int i=1;i<n;i++)
        {
            scanf("%lld%lld",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dp[1][0]=dp[1][1]=a[1];
        dfs1(1,-1);
        dfs2(1,-1);
        LL ans=dpdown[1][0][0];
        for(int i=2;i<=n;i++) 
        {
            if(d[i]) ans=max(ans,min(dp[i][0],dpdown[i][0][0]));
            else ans=max(ans,dp[i][0]);
        }
        printf("%lld\n",ans);
    }
} 

其他

Java大数

import java.util.*;
import java.text.*;
import java.math.*;
public class Main {
    static BigInteger[] num = new BigInteger[305];
    static BigInteger[] son = new BigInteger[305];
    static BigInteger[] mon = new BigInteger[305];
    static int[] p=new int[100005];
    static boolean[] prime=new boolean[100005];
    static int tot;
    static BigInteger gcd(BigInteger a,BigInteger b)
    {
        if(b.equals(BigInteger.ZERO)) return a;
        return gcd(b,a.mod(b));
    }
    public static void main(String[] args){
        Scanner cin=new Scanner(System.in);
        int t;
        t=cin.nextInt();
        for(int i=1;i<305;i++)prime[i]=true;
        for(int i=2;i<305;i++)
        {
            if(prime[i]) p[++tot]=i;
            for(int j=1;j<=tot&&i*p[j]<100005;j++)
            {
                prime[i*p[j]]=false;
                if (i%p[j]==0) break;
            }
        }
        num[1]=BigInteger.ONE;son[1]=BigInteger.ONE;mon[1]=BigInteger.ONE;
        for(int i=2;i<305;i++)
        {
            num[i]=num[i-1].multiply(BigInteger.valueOf(p[i-1]));
            son[i]=son[i-1].multiply(BigInteger.valueOf(p[i-1]));
            mon[i]=mon[i-1].multiply(BigInteger.valueOf(p[i-1]+1));
        }
        for(int i=1;i<=t;i++)
        {
            BigInteger n;
            n = cin.nextBigInteger();
            int j;
            for(j=1;j<305;j++)
            {
                if(num[j+1].compareTo(n)>0) break;
            }
            BigInteger g=gcd(mon[j],son[j]);
            System.out.print(son[j].divide(g));
            System.out.print('/');
            System.out.println(mon[j].divide(g));
        }
    }
}

stl收录

  1. __builtin_popcount(x)用于统计数字x的二进制表示中有多少位1

  2. next_permutation(a,a+n)用于求指针a到a+n之间元素的下一个排列

    if(a[u]+dpdown[v][0][0]==dpdown[u][1][0]) dp[v][0]=a[v]+dpdown[u][1][1];
    else dp[v][0]=a[v]+dpdown[u][1][0];
    if(u!=1) dp[v][0]=max(dp[v][0],a[v]+dp[u][1]);
    if(a[u]+dpdown[v][1][0]==dpdown[u][0][0]) dp[v][1]=a[v]+dpdown[u][0][1];
    else dp[v][1]=a[v]+dpdown[u][0][0];
    if(u!=1) dp[v][1]=min(dp[v][1],a[v]+dp[u][0]);
    }
    dfs2(v,u);
    }
    }
    int n;
    int32_t main()
    {
    int t;
    //freopen(“b.in”,“r”,stdin);
    scanf("%lld",&t);
    while(t–)
    {
    memset(d,0,sizeof(d));
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++) scanf("%lld",&tp),a[i]-=tp;
    for(int i=1;i<=n;i++) G[i].clear(),dpdown[i][0][0]=dpdown[i][0][1]=inf,dpdown[i][1][0]=dpdown[i][1][1]=-inf;
    int x,y;
    for(int i=1;i<n;i++)
    {
    scanf("%lld%lld",&x,&y);
    G[x].push_back(y);
    G[y].push_back(x);
    }
    dp[1][0]=dp[1][1]=a[1];
    dfs1(1,-1);
    dfs2(1,-1);
    LL ans=dpdown[1][0][0];
    for(int i=2;i<=n;i++)
    {
    if(d[i]) ans=max(ans,min(dp[i][0],dpdown[i][0][0]));
    else ans=max(ans,dp[i][0]);
    }
    printf("%lld\n",ans);
    }
    }


## 其他

### Java大数

```java
import java.util.*;
import java.text.*;
import java.math.*;
public class Main {
    static BigInteger[] num = new BigInteger[305];
    static BigInteger[] son = new BigInteger[305];
    static BigInteger[] mon = new BigInteger[305];
    static int[] p=new int[100005];
    static boolean[] prime=new boolean[100005];
    static int tot;
    static BigInteger gcd(BigInteger a,BigInteger b)
    {
        if(b.equals(BigInteger.ZERO)) return a;
        return gcd(b,a.mod(b));
    }
    public static void main(String[] args){
        Scanner cin=new Scanner(System.in);
        int t;
        t=cin.nextInt();
        for(int i=1;i<305;i++)prime[i]=true;
        for(int i=2;i<305;i++)
        {
            if(prime[i]) p[++tot]=i;
            for(int j=1;j<=tot&&i*p[j]<100005;j++)
            {
                prime[i*p[j]]=false;
                if (i%p[j]==0) break;
            }
        }
        num[1]=BigInteger.ONE;son[1]=BigInteger.ONE;mon[1]=BigInteger.ONE;
        for(int i=2;i<305;i++)
        {
            num[i]=num[i-1].multiply(BigInteger.valueOf(p[i-1]));
            son[i]=son[i-1].multiply(BigInteger.valueOf(p[i-1]));
            mon[i]=mon[i-1].multiply(BigInteger.valueOf(p[i-1]+1));
        }
        for(int i=1;i<=t;i++)
        {
            BigInteger n;
            n = cin.nextBigInteger();
            int j;
            for(j=1;j<305;j++)
            {
                if(num[j+1].compareTo(n)>0) break;
            }
            BigInteger g=gcd(mon[j],son[j]);
            System.out.print(son[j].divide(g));
            System.out.print('/');
            System.out.println(mon[j].divide(g));
        }
    }
}

stl收录

  1. __builtin_popcount(x)用于统计数字x的二进制表示中有多少位1
  2. next_permutation(a,a+n)用于求指针a到a+n之间元素的下一个排列
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值