1. 程式人生 > >Nobleman__ ACM 比賽模板 (C++ && Java)個人總結 (不斷更新) (自用)

Nobleman__ ACM 比賽模板 (C++ && Java)個人總結 (不斷更新) (自用)

  • 宣告 : 本人剛學演算法一年,都是自己做題常用的模板,不時總結下。

大致分為:亂七八糟, 數論,圖論,動態規劃,幾何,Java
還有一些奇葩定理,

奇葩定理:

【1】高效求出n的約數的個數:
對 數 X 做質因數分解, 結果假定是 1^1 * a ^ A * b ^ B .... z ^ Z , 那 X 的因數個數應該是 (A+1) * (B+1) * .... (Z+1) - 1
而 X^2 的因數個數應該是 (2*A + 1) * (2*B + 1) * ... (2*Z + 1) -1 ....

比如 12 可以寫做事 1 * 2^2 * 3, 所以 12 的因數數量是 (2+1) * (1+1) - 1 = 5 個, 分別是 1 2 3 4 6


而 12^2 = 144 有 (2*2+1) * (2*1+1) - 1 = 14 個, 分別是 1 2 3 4 6 8 9 12 16 18 24 36 48 72

【2】快速判斷Cnm : 如果 (n&m) == m Cnm為奇數

亂七八糟模板

  • 標頭檔案,巨集定義,讀入掛
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string> #include <bitset> #include <vector> #include <set> #include <map> #include <queue> #include <algorithm> #include <sstream> #include <stack> #include <iomanip> #define ll long long #define ull unsigned long long #define PI acos(-1.0)
#define eps 1e-12 #define fi first #define se second #define MEM(a,b) memset((a),(b),sizeof(a)) #define mod(x) ((x)%MOD) #define pii pair<int,int> #define wz cout<<"-----"<<endl; const int INF_INT = 2147483647; const ll INF_LL = 9223372036854775807LL; const ull INF_ULL = 18446744073709551615Ull; const ll P = 92540646808111039LL; const ll maxn = 1e5 + 10, MOD = 1e9 + 7; const int Move[4][2] = {-1,0,1,0,0,1,0,-1}; const int Move_[8][2] = {-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1}; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; }
  • 結構體過載運算子
//與平時cmp函式 相反
bool operator < (const node &p) const {
        return r > p.r;
    }

資料結構

  • 樹狀陣列

/*
單點更新,區間查詢,(也可區間更新加),逆序對等等
*/
int lowerbit(int x) {
    return x & -x;
}

void add(int p, int x) {
    while (p < maxn) {
        d[p] += x;
        p += lowerbit(p);
    }
}
int sum(int p) {
    int res = 0;
    while (p) {
        res += d[p];
        p -= lowerbit(p);
    }
    return res;
}

數論模板

  • 快速冪
ll quick_pow(ll a,ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res = a * res;
        }
        a = a * a;
        b >>= 1;
    }
    return res;
}
  • 矩陣快速冪
#include<bits/stdc++.h>
#define ll long long
#define mod(x) ((x)%MOD)

using namespace std;

const ll MOD = 1e9 + 7;

struct mat{
    ll m[3][3];
}a,ans,unit;

void init() {
    memset(unit.m,0,sizeof(unit.m));
    memset(a.m,0,sizeof(a.m));
    unit.m[0][0] = 1;
    unit.m[1][1] = 1;
    a.m[0][0] = 3;
    a.m[0][1] = 1;
    a.m[1][0] = 1;
    a.m[1][1] = 3;
}

mat operator * (mat m1,mat m2) {
    mat t;
    ll r;
    for(int i = 0;i < 3;i++) {
        for(int j = 0;j < 3;j++) {
            r = 0;
            for(int k = 0;k < 3;k++) {
                r = mod(r*1ll + mod(mod(m1.m[i][k])*1ll*mod(m2.m[k][j])));
            }
            t.m[i][j] = r;
        }
    }
    return t;
}

mat quick_pow(ll x) {
    mat t = unit;
    while(x) {
        if(x & 1) {
            t = t*a;
        }
        a = a*a;
        x >>= 1;
    }
    return t;
}
int main(){
    init();
    ans = quick_pow(n);
}
  • 素數的兩種篩法
bool isp[maxn];
int p[maxn], len;

bool isp[100];
void init() {
    int m = (int)sqrt(maxn+0.5);
    for(int i = 2;i <= m;i++) {
        if(!isp[i]) {
            for(int j = i*i;j <= maxn;j += i) {
                isp[j] = true;
            }
        }
    }
}

void init() { //推薦這個,較快
    isp[0] = isp[1] = true;
    for (int i = 2; i < maxn; i++) {
        if(!isp[i]) p[++len] = i;
        for (int j = 1; j <= len && p[j]*i < maxn; j++) {
            isp[i*p[j]] = true;
            if (i%p[j] == 0) break;
        }
    }
}
  • 尤拉函式
    在數論,對正整數n,尤拉函式是小於n的正整數中與n互質的數的數目(φ(1)=1)。
int euler_phi(int n){ //單個值
    int m = (int)sqrt(n + 0.5);
    int ans = n;
    for (int i = 2;i <= m;i++){
        if (n%i == 0){       //如果存在素因子
            ans = ans/i*(i-1); 
            while (n%i == 0) n/=i;
        }
    }
    if(n > 1) ans = ans/n*(n-1); //考慮n本身
    return ans;
}

void phi_table(int n,int *phi){ //尤拉表
    for (int i = 1;i <= n;i++) phi[i] = i;
    for(int i = 2;i <= n;i++){
        if(phi[i] == i){   //類似於Eratosthenes篩法這裡
            for(int j = i;j <= n;j+=i){
                phi[j] = phi[j]/i*(i-1);
            }
        }
    }       
}
  • 歐幾里得GCD,擴充套件~
    歐幾里德演算法又稱輾轉相除法,用於計算兩個整數a,b的最大公約數(greatest common divisor)。
    擴充套件歐幾里德演算法是用來在已知a, b求解一組x,y,使它們滿足貝祖等式: ax+by = gcd(a, b) =d(解一定存在,根據數論中的相關定理)。擴充套件歐幾里德常用在求解模線性方程及方程組中。
ll gcd(ll a,ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

void egcd(ll a, ll b, ll &d, ll &x, ll &y) {
    if(!b) d=a,x=1,y=0;
    else egcd(b, a % b, d, y, x),y -= x * (a / b);
}
  • 逆元
    方程ax≡1(mod p),的解稱為a關於模p的逆,當gcd(a,p)==1(即a,p互質)時,方程有唯一解,否則無解。
    對於一些題目會要求把結果MOD一個數,通常是一個較大的質數,對於加減乘法通過同餘定理可以直接拆開計算,但對於(a/b)%MOD這個式子,是不可以寫成(a%MOD/b%MOD)%MOD的,但是可以寫為(a*b^-1)%MOD,其中b^-1表示b的逆元。
ll getinv (ll a,ll p) {
    ll d, x, y;
    egcd (a, p, d, x, y);
    return (x + p) % p == 0 ? p : (x + p) % p;
}
  • 中國剩餘定理(CRT) ,擴充套件~

中國剩餘定理給出了以下的一元線性同餘方程組:

這裡寫圖片描述
假設整數m1,m2, ... ,mn兩兩互質,則對任意的整數:a1,a2, ... ,an,方程組 有解,即x,擴充套件剩餘定理就是m1,m2···mn,這幾個數不兩兩互質的情況

// M 為 lcm(m1,m2...,mn)
ll CRT(ll M){
    ll sum=0,tmp,v;
    for (int i=1;i<=cnt;i++){
        tmp=M/m[i];
        v=getInv(tmp,m[i]);
        sum=(sum+tmp*a[i]*v)%M;
    }
    return sum;
}

/*以下是ECRT*/
///這裡的是從下標是從1開始, r陣列代表 餘數,m 陣列代表除數
bool merge(ll &a1,ll &m1,ll a2,ll m2){
    ll c,d,x,a3,m3;
    c=a2-a1;d=__gcd(m1,m2);
    if (c%d!=0) return false;
    c=c/d;m1=m1/d;m2=m2/d;
    x=getinv(m1,m2);
    x=(x*c)%m2;
    x=x*(m1*d)+a1;
    m3=m1*m2*d;
    a3=(x%m3+m3)%m3;
    a1=a3;m1=m3;
    return true;
}
ll ECRT(){
    ll A=r[1],M=m[1];
    for (int i=2;i<=n;i++) //無解返回 -1
      if (!merge(A,M,r[i],m[i]))
        return -1;
    return (A%M+M)%M;
}
  • 錯排公式
    問題: 十本不同的書放在書架上。現重新擺放,使每本書都不在原來放的位置。有幾種擺法?
    這個問題推廣一下,就是錯排問題,是組合數學中的問題之一。考慮一個有n個元素的排列,若一個排列中所有的元素都不在自己原來的位置上,那麼這樣的排列就稱為原排列的一個錯排。 n個元素的錯排數記為D(n)。 研究一個排列錯排個數的問題,叫做錯排問題或稱為更列問題。
//dp[i] = (i - 1)*(dp[i - 1] + dp[i - 2]); i > 2
ll a = 0,b = 1,c;
for (int i = 3; i <= n; i++) {
    c = ((i - 1) * 1ll * (a + b)) % MOD;
    a = b;
    b = c;
}
printf("%lld\n",c);
  • O(n) 求組合數
    Cnk=nk+1kCnk1
    從開始從左到右遞推,注意爆int

C[0] = 1;
for(int i = 1; i <= n; i++) 
    C[i] = C[i - 1] * (n - i + 1) / i;
  • 卡特蘭數列
    h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
    h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)
    C(m+n,n)−C(m+n,n−1)

  • 階乘逆元

fac[0] = 1;
for (int i = 1; i <= maxn; i++) 
    fac[i] = mod(fac[i - 1] * i);
rfac[maxn] = qpow(fac[maxn],MOD - 2);
for (int i = maxn;i > 0; i--) 
    rfac[i - 1] = mod(rfac[i] * i);
  • Lucas

Cnm%p,其中p為質數

n=nkpk+nk1pk1+...+n2p2+n1p+n0
m=mkpk+mk1pk1+...+m2p2+m1p+m0m

Cnm=i=0kCnimi

ll C(ll nn, ll mm) {
    ll up = 1, down = 1;
    for (int i = nn - mm + 1; i <= nn; i++) up = mod(up * 1ll *i);
    for (int i = 1; i <= mm; i++) down = mod(down * 1ll *i);
    return mod(up * qpow(down, MOD - 2));
}

ll lucas(ll n, ll m) {
    if(m == 0) return 1;
    return mod(lucas(n / MOD, m / MOD) * 1ll * C(n % MOD,m % MOD));
}
  • 斯特林公式,計算階乘
    公式如下:

N!=2πn(ne)n

化簡如下: