数论模板汇总(下)

37.排列组合

不可重复排列数 n ! ( n − r ) ! \frac{n!}{(n-r)!} (nr)!n!
可重复排列数 n r n^r nr
圆排列数 n ! r ( n − r ) ! \frac{n!}{r(n-r)!} r(nr)!n!
无限多重集排列,k个不同元素 k r k^r kr
有限多重集排列,k个不同元素 n ! n 1 ! n 2 ! . . . n k ! \frac{n!}{n_1!n_2!...n_k!} n1!n2!...nk!n!
无限多重集组合,k个不同元素 C r + k − 1 r C_{r+k-1}^r Cr+k1r

38.鸽巢原理

n+1只鸽子住n个巢,至少有一个巢里有至少2只鸽子
kn+1只鸽子住n个巢,至少有一个巢里有至少k+1只鸽子
如果将 q 1 + q 2 + . . . q n − n + 1 q_1+q_2+...q_n-n+1 q1+q2+...qnn+1个物体放入n个盒子,那么或者第1个盒子至少有 q 1 q_1 q1个物体,或者第2个盒子至少有 q 2 q_2 q2个物体,…,或者第n个盒子至少有 q n q_n qn个物体
Ramsey定理:在6人(或更多)中,或者有3人,每两人都互相认识,或者有3人,每两人都不认识

39.杨辉三角与二项式定理

C n r = ( n r ) = n ! r ! ( n − r ) ! C_n^r=\binom{n}{r}=\frac{n!}{r!(n-r)!} Cnr=(rn)=r!(nr)!n!
杨辉三角第n+1行第r+1列数, ( 1 + x ) n (1+x)^n (1+x)n展开后第r+1项系数
( a + b ) n = ∑ r = 0 n C n r a r b n − r (a+b)^n=\sum\limits_{r=0}^nC_n^ra^rb^{n-r} (a+b)n=r=0nCnrarbnr
C n r = C n − 1 r − 1 + C n − 1 r C_n^r=C_{n-1}^{r-1}+C_{n-1}^r Cnr=Cn1r1+Cn1r
C n 0 + C n 1 + . . . + C n n = 2 n C_n^0+C_n^1+...+C_n^n=2^n Cn0+Cn1+...+Cnn=2n

40.组合数(递推)

#include<bits/stdc++.h>
using namespace std;
const int N = 2010, mod = 1e9 + 7;
int c[N][N];
void init(){
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j <= i; j ++ )
            if (j==0) c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
int main(){
    int n;
    init();
    scanf("%d", &n);
    while (n -- ){
        int a, b;
        scanf("%d%d", &a, &b);
        printf("%d\n", c[a][b]);
    }
    return 0;
}

41.组合数(费马小定理/预处理)

预处理 O ( n ) O(n) O(n)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 998244353;
const ll N = 1e6 + 10;
ll jc[N], jcn[N],inv[N];;
//ll ksm(ll a, ll b) {
//    ll res = 1;
//    a = a % mod;
//    while (b) {
//        if (b & 1) res = (res * a) % mod;
//        a = (a * a) % mod;
//        b >>= 1;
//    }
//    return res;
//}
//费马小定理
//ll ny(ll x) {
//    return ksm(x, mod - 2);
//}
void pre_ny(ll n, ll p) {
    inv[1] = 1;
    for (ll i = 2; i <= n; i++) inv[i] = (p - p / i) * (inv[p % i]) % p;//线性求逆元
}
ll C(ll n, ll m) {
    return jc[n] * jcn[m] % mod * jcn[n - m] % mod;
}
int main() {
    pre_ny(N, mod);
    jc[0] = jcn[0] = 1;
    for (ll i = 1; i <= N; i++) {
        jc[i] = (jc[i - 1] * i) % mod;
        jcn[i] = (jcn[i - 1] * inv[i]) % mod;
    }
    ll t, n, m;
    cin>>t;
    while (t--) {
        cin >> n >> m;
        cout<< C(n, m)<<'\n';
    }
    return 0;
}

42.卢卡斯定理

对于质数 p,有
C n r ≡ C n   m o d   p r   m o d   p ⋅ C n / p r / p ( m o d   p ) C_n^r\equiv C_{n\ mod\ p}^{r\ mod\ p}\cdot C_{n/p}^{r/p}(mod\ p) CnrCn mod pr mod pCn/pr/p(mod p)

P3807 【模板】卢卡斯定理/Lucas 定理
给定整数 n , m , p n, m, p n,m,p 的值,求出 C n + m n   m o d   p C_{n + m}^n \bmod p Cn+mnmodp 的值。
输入数据保证 p p p 为质数。
注: C C C 表示组合数。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
const ll mod = 1e9 + 7;
ll jc[N];
ll ksm(ll a, int b, int p) {
    a %= p;
    ll res = 1;
    while (b) {
        if (b & 1) res = (res * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return res;
}
ll C(int n, int r, int p) {
    if (r > n) return 0;
    return jc[n] * ksm(jc[r], p - 2, p) % p * ksm(jc[n - r], p - 2, p) % p;
}
ll lucas(int n, int r, int p) {
    if (r == 0) return 1;
    return C(n % p, r % p, p) * lucas(n / p, r / p, p) % p;
}
void solve() {
    int n, m, p;
    cin >> n >> m >> p;
    jc[0] = 1;
    for (int i = 1; i <= p; ++i) jc[i] = (jc[i - 1] * i) % p;
    cout << lucas(n + m, n, p) << '\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    //_t = 1;
    cin >> _t;
    while (_t--) {
        solve();
    }
    return 0;
}

43.扩展卢卡斯定理

{ C n m   m o d     p 1 α 1 C n m   m o d     p 2 α 2 . . . . . . \left\{\begin{aligned}C_n^m \bmod\ {p_1^{\alpha_1}} \\C_n^m \bmod\ {p_2^{\alpha_2}}\\ ......\end{aligned} \right. Cnmmod p1α1Cnmmod p2α2......
= n ! P x m ! P y ( n − m ) ! P z P x − y − z   m o d   P k =\frac {\frac {n!}{P^{x}}}{\frac {m!}{P^{y}}\frac {(n-m)!}{P^{z}}}P^{x-y-z}\bmod{P^k} =Pym!Pz(nm)!Pxn!PxyzmodPk
= f ( n ) f ( m ) f ( n − m ) P g ( n ) − g ( m ) − g ( n − m )   m o d   P k =\frac {f(n)}{f(m)f(n-m)}P^{g(n)-g(m)-g(n-m)}\bmod{P^k} =f(m)f(nm)f(n)Pg(n)g(m)g(nm)modPk
  f ( n ) = f ( ⌊ n P ⌋ ) ( ∏ i = 1 , i ≢ 0 ( m o d P ) P k i ) ⌊ n P k ⌋ ( ∏ i = P k ⌊ n P k ⌋ , i ≢ 0 ( m o d P ) n i ) \ f(n)=f(\lfloor \frac nP\rfloor)(\prod\limits_{i=1,i\not\equiv 0\pmod {P}}^{P^k}i)^{\lfloor \frac n{P^k}\rfloor}(\prod\limits_{i=P^k\lfloor \frac n{P^k}\rfloor,i\not\equiv 0\pmod {P}}^ni)  f(n)=f(⌊Pn⌋)(i=1,i0(modP)Pki)Pkn(i=PkPkn,i0(modP)ni)
g ( n ) = ⌊ n P ⌋ + g ( ⌊ n P ⌋ ) g(n)=\lfloor \frac nP\rfloor+g(\lfloor \frac nP\rfloor) g(n)=Pn+g(⌊Pn⌋)

P4720 【模板】扩展卢卡斯定理/exLucas
C n m   m o d   p {\mathrm{C}}_n^m \bmod{p} Cnmmodp
其中 C \mathrm{C} C 为组合数。

O ( log ⁡ P n ) O(\log_Pn) O(logPn)

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e5 + 10;
ll n, m, p;
ll prime[N], cnt, c[N];
ll ksm(ll a, ll b, ll p) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}
ll exgcd(ll a, ll b, ll& x, ll& y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    ll t = exgcd(b, a % b, x, y);
    ll x0 = x, y0 = y;
    x = y0;
    y = x0 - a / b * y0;
    return t;
}
ll ny(ll n, ll p) {
    ll x, y;
    exgcd(n, p, x, y);
    return (x % p + p) % p;
}
ll f(ll n, ll p, ll pr) {
    if (n == 0) return 1;
    ll rou = 1, rem = 1;
    for (ll i = 1; i <= p; ++i) {
        if (i % pr) rou = rou * i % p;
    }
    rou = ksm(rou, n / p, p);
    for (ll i = p * (n / p); i <= n; ++i) {
        if (i % pr) rem = rem * (i % p) % p;
    }
    return f(n / pr, p, pr) * rou % p * rem % p;
}
ll g(ll n, ll p) {
    if (n < p) return 0;
    return g(n / p, p) + n / p;
}
ll C(ll n, ll m, ll p, ll pr) {
    ll fz = f(n, p, pr), fm1 = ny(f(m, p, pr), p), fm2 = ny(f(n - m, p, pr), p);
    ll pp = ksm(pr, g(n, pr) - g(m, pr) - g(n - m, pr), p);
    return fz * fm1 % p * fm2 % p * pp % p;
}
void getprime(int x) {
    for (ll i = 2; i * i <= x; ++i) {
        if (x % i == 0) {
            ll res = 1;
            while (x % i == 0) {
                res *= i;
                x /= i;
            }
            prime[++cnt] = res;
            c[cnt] = C(n, m, res, i);
        }
    }
    if (x != 1) {
        prime[++cnt] = x;
        c[cnt] = C(n, m, x, x);
    }
}
ll exlucas(ll n, ll m, ll p) {
    getprime(p);
    ll ans = 0;
    for (ll i = 1; i <= cnt; ++i) {
        ll m = p / prime[i], inv = ny(m, prime[i]);
        ans = (ans + c[i] * m % p * inv % p) % p;
    }
    return ans;
}
void solve() {
    cin >> n >> m >> p;
    cout << exlucas(n, m, p);
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    _t = 1;
    //cin>>_t;
    while (_t--) {
        solve();
    }
    return 0;
}

44.容斥原理

∣ A 1 ∪ A 2 ∪ . . . ∪ A n ∣ = ∑ ∣ A i ∣ − ∑ ∣ A i ∩ A j ∣ + ∑ ∣ A i ∩ A j ∩ A k ∣ + . . . + ( − 1 ) n + 1 ∣ A 1 ∩ A 2 ∩ . . . ∩ A n ∣ |A_1\cup A_2\cup...\cup A_n|=\sum|A_i|-\sum|A_i\cap A_j|+\sum|A_i\cap A_j\cap A_k|+...+(-1)^{n+1}|A_1\cap A_2\cap...\cap A_n| A1A2...An=AiAiAj+AiAjAk+...+(1)n+1A1A2...An

45.卡特兰数

C n = 1 n + 1 ( 2 n n ) C_n=\frac{1}{n+1}\binom{2n}{n} Cn=n+11(n2n)
C n = ( 2 n n ) − ( 2 n n − 1 ) C_n=\binom{2n}{n}-\binom{2n}{n-1} Cn=(n2n)(n12n)
C n = C 0 C n − 1 + C 1 C n − 2 + . . . + C n − 2 C 1 + C n − 1 C 0 = ∑ C k C n − k , C 0 = 1 C_n=C_0C_{n-1}+C_1C_{n-2}+...+C_{n-2}C_1+C_{n-1}C_0=\sum C_kC_{n-k},C_0=1 Cn=C0Cn1+C1Cn2+...+Cn2C1+Cn1C0=CkCnk,C0=1
C n = 4 n − 2 n + 1 C n − 1 , C 0 = 1 C_n=\frac{4n-2}{n+1}C_{n-1},C_0=1 Cn=n+14n2Cn1,C0=1
把n个1和n个0排成一行,使任意前k个数中1的数量总大于等于0的数量的排列数量
应用:
1.棋盘问题(路径问题)
2.括号问题(出栈序列问题)
3.二叉树问题
4.三角剖分问题:n+1条边凸多边形划分三角形,有 C n − 1 C_{n-1} Cn1种方案

46.第一类斯特林数

s ( n , k ) , [ n k ] : s(n,k),{n\brack k}: s(n,k),[kn]: 把n个不同元素分配到k个圆排列里的方案数
升阶函数各项系数
[ n k ] = [ n − 1 k − 1 ] + ( n − 1 ) [ n − 1 k ] {n\brack k}={n-1\brack k-1}+(n-1){n-1\brack k} [kn]=[k1n1]+(n1)[kn1]
s ( 0 , 0 ) = 1 s(0,0)=1 s(0,0)=1
s ( n , 0 ) = 0 s(n,0)=0 s(n,0)=0
s ( n , n ) = 1 s(n,n)=1 s(n,n)=1
s ( n , 1 ) = ( n − 1 ) ! s(n,1)=(n-1)! s(n,1)=(n1)!
s ( n , 2 ) = ( n − 1 ) ! ∑ i = 1 n − 1 1 i s(n,2)=(n-1)!\sum\limits_{i=1}^{n-1}\frac{1}{i} s(n,2)=(n1)!i=1n1i1
s ( n , n − 1 ) = C n 2 s(n,n-1)=C_n^2 s(n,n1)=Cn2
s ( n , n − 2 ) = 2 C n 3 + 3 C n 4 s(n,n-2)=2C_n^3+3C_n^4 s(n,n2)=2Cn3+3Cn4
∑ k = 0 n s ( n , k ) = n ! \sum\limits_{k=0}^ns(n,k)=n! k=0ns(n,k)=n!

47.第二类斯特林数

S ( n , k ) , { n k } : S(n,k),{n\brace k}: S(n,k),{kn}: 把n个不同元素拆分成k个集合的方案数
{ n k } = { n − 1 k − 1 } + k { n − 1 k } {n\brace k}={n-1\brace k-1}+k{n-1\brace k} {kn}={k1n1}+k{kn1}
S ( n , m ) = 1 m ! ∑ k = 0 m ( − 1 ) k ( m k ) ( m − k ) n S(n,m)=\frac{1}{m!}\sum\limits_{k=0}^m(-1)^k\binom{m}{k}(m-k)^n S(n,m)=m!1k=0m(1)k(km)(mk)n
S ( n , 0 ) = 0 n S(n,0)=0^n S(n,0)=0n
S ( n , n ) = 1 S(n,n)=1 S(n,n)=1
S ( n , 1 ) = 1 S(n,1)=1 S(n,1)=1
S ( n , 2 ) = 2 n − 1 − 1 S(n,2)=2^{n-1}-1 S(n,2)=2n11
S ( n , n − 1 ) = C n 2 S(n,n-1)=C_n^2 S(n,n1)=Cn2
S ( n , n − 2 ) = C n 3 + 3 C n 4 S(n,n-2)=C_n^3+3C_n^4 S(n,n2)=Cn3+3Cn4
∑ k = 0 n S ( n , k ) = B n , B n \sum\limits_{k=0}^nS(n,k)=B_n,B_n k=0nS(n,k)=Bn,Bn是贝尔数
∑ k = 0 n { n k } [ k m ] = ∑ k = 0 n [ n k ] { k m } \sum\limits_{k=0}^n{n\brace k}{k\brack m}=\sum\limits_{k=0}^n{n\brack k}{k\brace m} k=0n{kn}[mk]=k=0n[kn]{mk}

48.贝尔数

B n B_n Bn的含义是基数为 n 的集合划分成非空集合的划分数
B 0 = 1 B_0=1 B0=1
B n + 1 = ∑ k = 0 n ( n k ) B k B_{n+1}=\sum\limits_{k=0}^n\binom{n}{k}B_k Bn+1=k=0n(kn)Bk
B n = ∑ k = 0 n S ( n , k ) B_n=\sum\limits_{k=0}^nS(n,k) Bn=k=0nS(n,k)
第一行第一项为 1 ( a 1 , 1 = 1 ) (a_{1,1}=1) (a1,1=1)
对于 n>1,第 n 行第一项等于第 n-1 行的第 n - 1 项 ( a n , 1 = a n − 1 , n − 1 ) (a_{n,1}=a_{n-1,n-1}) (an,1=an1,n1)
对于 m,n>1,第 n 行的第 m 项等于它左边和左上角两个数之和 ( a n , m = a n , m − 1 + a n − 1 , m − 1 ) (a_{n,m}=a_{n,m-1}+a_{n-1,m-1}) (an,m=an,m1+an1,m1)
每行的首项是贝尔数。

49.错位排列

D n = ( n − 1 ) ( D n − 1 + D n − 2 ) D_n=(n-1)(D_{n-1}+D_{n-2}) Dn=(n1)(Dn1+Dn2)
D n = n ! ∑ k = 0 n ( − 1 ) k k ! D_n=n!\sum\limits_{k=0}^n\frac{(-1)^k}{k!} Dn=n!k=0nk!(1)k

50.Burnside定理

置换的合成运算 g ∘ f ( k ) = g ( f ( k ) ) g\circ f(k)=g(f(k)) gf(k)=g(f(k))
置换的逆 f − 1 ∘ f = τ f^{-1}\circ f=\tau f1f=τ τ \tau τ为恒等置换
设G是S的置换群,f是G中的一个置换,设 λ ( f ) \lambda(f) λ(f)为置换f中不变元的个数,则置换群G的着色方法数为 1 ∣ G ∣ ∑ f ∈ G ∣ λ ( f ) ∣ \frac{1}{|G|}\sum\limits_{f\in G}|\lambda(f)| G1fGλ(f)

P1446 [HNOI2008] Cards
小春现在很清闲,面对书桌上的 n n n 张牌,他决定给每张牌染色,目前小春拥有 3 3 3 种颜色:红色,蓝色,绿色。他询问 Sun 有多少种染色方案,Sun 很快就给出了答案。
进一步,小春要求染出 S r S_r Sr 张红色, S b S_b Sb 张蓝色, S g S_g Sg 张绿色。他又询问有多少种方案,Sun 想了一下,又给出了正确答案。最后小春发明了 m m m 种不同的洗牌法,这里他又问 Sun 有多少种不同的染色方案。两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种。
Sun 发现这个问题有点难度,决定交给你,由于答案可能很大,你只需要求出答案对于 P P P 取模的结果。 保证 P P P 为一个质数。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e5 + 10;
int sr, sb, sg, n, m, p, ans;
int a[100], vis[100], siz[100], dp[25][25][25];
ll ksm(ll a, ll b) {
    a %= p;
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}
int calc() {
    memset(dp, 0, sizeof(dp));
    memset(vis, 0, sizeof(vis));
    int cnt = 0;
    for (int i = 1; i <= n; ++i) {
        if (!vis[i]) {
            int p = i, len = 0;
            while (!vis[p]) {
                len++;
                vis[p] = 1;
                p = a[p];
            }
            siz[++cnt] = len;
        }
    }
    dp[0][0][0] = 1;
    for (int i = 1; i <= cnt; ++i) {
        for (int r = sr; r>=0; --r) {
            for (int b = sb; b>=0; --b) {
                for (int g = sg; g>=0; --g) {
                    if (r >= siz[i]) dp[r][b][g] = (dp[r][b][g] + dp[r - siz[i]][b][g]) % p;
                    if (b >= siz[i]) dp[r][b][g] = (dp[r][b][g] + dp[r][b - siz[i]][g]) % p;
                    if (g >= siz[i]) dp[r][b][g] = (dp[r][b][g] + dp[r][b][g - siz[i]]) % p;
                }
            }
        }
    }
    return dp[sr][sb][sg];
}
void solve() {
    cin >> sr >> sb >> sg >> m >> p;
    n = sr + sb + sg;
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            cin >> a[j];
        }
        ans = (ans + calc()) % p;
    }
    for (int i = 1; i <= n; ++i) a[i] = i;
    ans = (ans + calc()) % p;
    cout << ans * ksm(m + 1, p - 2) % p;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    _t = 1;
    //cin>>_t;
    while (_t--) {
        solve();
    }
    return 0;
}

51.Pólya 定理

任何一种置换都能表示成若干互不相交的循环的乘积。
在一种置换中,用 λ i ( f ) \lambda_i(f) λi(f) 表示长度为 i 的循环的个数,用 λ ( f ) \lambda(f) λ(f)表示 f ∈ G f\in G fG中循环的个数。
G = { f 1 , f 2 , . . . , f n } G=\{f_1,f_2,...,f_n\} G={f1,f2,...,fn} S n S_n Sn是置换群,用 m 种颜色对 n 个对象着色,G的着色方法数为 1 ∣ G ∣ ∑ f ∈ G m λ ( f ) = 1 ∣ G ∣ ∑ f ∈ G m λ 1 ( f ) + λ 2 ( f ) + . . . + λ n ( f ) \frac{1}{|G|}\sum\limits_{f\in G}m^{\lambda(f)}=\frac{1}{|G|}\sum\limits_{f\in G}m^{\lambda_1(f)+\lambda_2(f)+...+\lambda_n(f)} G1fGmλ(f)=G1fGmλ1(f)+λ2(f)+...+λn(f)
第 i 个旋转的循环节数为 g c d ( n , i ) , 0 ≤ i < n gcd(n,i),0\le i<n gcd(n,i),0i<n
若 n 为偶数,经过多边形顶点的对称轴有n/2个,即n/2种置换,每种置换方案有n/2+1个循环节;不经过顶点的对称轴有n/2个,每种置换方案有n/2个循环节
若 n 为奇数,对称轴都经过顶点,有n种置换,每种置换有n/2+1个循环节

P4980 【模板】Pólya 定理
给定一个 n n n 个点, n n n 条边的环,有 n n n 种颜色,给每个顶点染色,问有多少种本质不同的染色方案,答案对 1 0 9 + 7 10^9+7 109+7 取模
注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方案相同

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e5 + 10;
ll getphi(ll n) {
    ll res = n;
    for (ll i = 2; i*i<=n; ++i) {
        if (n % i == 0) {
            res = res / i * (i - 1);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) res = res / n * (n - 1);
    return res;
}
ll ksm(ll a, ll b) {
    a %= mod;
    ll res = 1;
    while (b) {
        if (b & 1) res = (res * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return res;
}
void solve() {
    ll n,ans=0;
    cin >> n;
    for (ll i = 1; i * i <= n; ++i) {
        if (n % i == 0) {
            ans = (ans + getphi(i) * ksm(n, n / i - 1)) % mod;
            if (i * i != n) ans = (ans + getphi(n / i) * ksm(n, i - 1)) % mod;
        }
    }
    cout << ans << '\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    //_t = 1;
    cin>>_t;
    while (_t--) {
        solve();
    }
    return 0;
}

52.生成函数

生成函数的定义: g ( x ) = a 0 + a 1 x + a 2 x 2 + a 3 x 3 + . . . g(x)=a_0+a_1x+a_2x^2+a_3x^3+... g(x)=a0+a1x+a2x2+a3x3+....称g(x)是序列 a 0 , a 1 , a 2 , . . . a_0,a_1,a_2,... a0,a1,a2,...的生成函数,x 充当形式参数没有意义。
普通生成函数:k 种元素的多重集合的 r 组合数(有限和无限多重集都可以)
指数生成函数:k 种元素的多重集合的 r 排列数(有限和无限多重集都可以)

列出式子,有 n 项,每项代表一个种类的限制(一个括号代表一项),共取 m 个,生成函数为 g ( x ) = ( 1 + x + x 2 + …   ) ( 1 + x 2 + x 4 + …   ) ( 1 + x 3 + x 6 + …   ) … g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^3+x^6+\dots)\dots g(x)=(1+x+x2+)(1+x2+x4+)(1+x3+x6+)计算 x m x^m xm 的系数,化为分式,二项式展开。

  1. HDU - 2152 Fruit (普通生成函数)
    有 n 种水果,每种水果出现的次数在 [ a i , b i ] [a_i,b_i] [ai,bi] 之间,问一共买 m 个水果有多少种购买方案?
    ( x a 1 + ⋯ + x b 1 ) ( x a 2 + ⋯ + x b 2 ) … ( x a n + ⋯ + x b n ) (x^{a_1}+\dots+x^{b_1})(x^{a_2}+\dots+x^{b_2})\dots(x^{a_n}+\dots+x^{b_n}) (xa1++xb1)(xa2++xb2)(xan++xbn)对这个生成函数求 x m x^m xm 的系数
  2. HDU - 1521 排列组合 (指数生成函数)
    有 n 种物品,每种物品的数量为 a i a_i ai ,问取 m 个物品的排列数
    ( 1 + x + x 2 2 ! + ⋯ + x a 1 a 1 ! ) ( 1 + x + x 2 2 ! + ⋯ + x a 2 a 2 ! ) … ( 1 + x + x 2 2 ! + ⋯ + x a n a n ! ) (1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_1}}{a_1!})(1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_2}}{a_2!})\dots(1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_n}}{a_n!}) (1+x+2!x2++a1!xa1)(1+x+2!x2++a2!xa2)(1+x+2!x2++an!xan)对这个式子求 x m x^m xm 的系数
  3. HDU - 1028 Ignatius and the Princess III (普通生成函数)
    将整数 n 拆分成正整数相加的形式,问有几种组合
    每一个小于 n 的正整数都有可能组成。共有 n 个因子,可以得到生成函数
    g ( x ) = ( 1 + x + x 2 + …   ) ( 1 + x 2 + x 4 + …   ) ( 1 + x 3 + x 6 + …   ) … g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^3+x^6+\dots)\dots g(x)=(1+x+x2+)(1+x2+x4+)(1+x3+x6+)
    x n x^n xn的系数就是所求的组合方式
  4. HDU - 1085 Holding Bin-Laden Captive! (普通生成函数)
    由面值为1、2、5的硬币不能组成的最小面值是多少。
    可以得到生成函数 g ( x ) = ( 1 + x + x 2 + …   ) ( 1 + x 2 + x 4 + …   ) ( 1 + x 5 + x 10 + …   ) g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^5+x^{10}+\dots) g(x)=(1+x+x2+)(1+x2+x4+)(1+x5+x10+)从小到大遍历系数,为 0 的那一项就是不能组成的
  5. HDU - 2189 悼念512汶川大地震遇难同胞——来生一起走 (普通生成函数)
    把n个人分成几个小组。每个小组的人数必须是素数
    用素数组成n,但是不知道用具体多少个素数。每一个小于等于 n 的素数都是一个因子。可以得到生成函数: g ( x ) = ( 1 + x 2 + x 4 + x 6 + …   ) ( 1 + x 3 + x 6 + …   ) ( 1 + x 5 + x 10 + …   )   x n g(x)=(1+x^2+x^4+x^6+\dots)(1+x^3+x^{6}+\dots)(1+x^5+x^{10}+\dots)\ x^n g(x)=(1+x2+x4+x6+)(1+x3+x6+)(1+x5+x10+) xn的系数就是这个组合数的答案
  6. BZOJ3028: 食物(普通生成函数 + 推导 + 欧拉降幂)
    明明这次又要出去旅游了,和上次不同的是,他这次要去宇宙探险!我们暂且不讨论他有多么NC,他又幻想了他应该带一些什么东西。理所当然的,你当然要帮他计算携带N件物品的方案数。他这次又准备带一些受欢迎的食物,如:蜜桃多啦,鸡块啦,承德汉堡等等。每种食物的限制如下:
    (1)承德汉堡:偶数个(2)可乐:0个或1个(3)鸡腿:0个,1个或2个(4)蜜桃多:奇数个(5)鸡块:4的倍数个(6)包子:0个,1个,2个或3个(7)土豆片炒肉:不超过一个。(8)面包:3的倍数个
    很明显示生成函数的题,并且考虑的是组合数,可以得到生成函数:思路:很明显示生成函数的题,并且考虑的是组合数
    可以得到生成函数: g ( x ) = ( 1 + x 2 + …   ) ( 1 + x ) ( 1 + x + x 2 ) ( x + x 3 + …   ) ( 1 + x 4 + x 8 + …   ) ( 1 + x + x 2 + x 3 ) ( 1 + x ) ( 1 + x 3 + x 6 + …   ) = 1 1 − x 2 ( 1 + x ) 1 − x 3 1 − x x 1 1 − x 2 1 1 − x 4 ( 1 − x 4 1 − x ) ( 1 + x ) 1 1 − x 3 = x ( 1 − x ) 4 = ∑ n = 0 ∞ C n + 4 − 1 3 x n + 1 = ∑ n = 0 ∞ C n + 2 3 x n g(x)=(1+x^2+\dots )(1+x)(1+x+x^2)(x+x^3+\dots)(1+x^4+x^8+\dots)(1+x+x^2+x^3)(1+x)(1+x^3+x^6+\dots)=\frac 1{1-x^2}(1+x)\frac {1-x^3}{1-x}x\frac 1{1-x^2}{\frac {1}{1-x^4}}(\frac {1-x^4}{1-x})(1+x)\frac 1{1-x^3}=\frac x{(1-x)^4}=\sum_{n=0}^{\infty}C_{n+4-1}^3x^{n+1}=\sum_{n=0}^{\infty}C_{n+2}^3x^n g(x)=(1+x2+)(1+x)(1+x+x2)(x+x3+)(1+x4+x8+)(1+x+x2+x3)(1+x)(1+x3+x6+)=1x21(1+x)1x1x3x1x211x41(1x1x4)(1+x)1x31=(1x)4x=n=0Cn+413xn+1=n=0Cn+23xn
    因此,答案就是 a n = C n + 2 3 a_n=C_{n+2}^3 an=Cn+23
  7. 2019 ICPC上海网络赛 E. Counting Sequences II (指数生成函数 + 推导)
    给定n个位置,对于每一个数 a i ​ a_i​ ai,都满足 1 ≤ a i ≤ m 1 ≤ a_i ≤ m 1aim,并且如果 a i a_i ai是偶数的话, a i a_i ai出现的次数是偶数次,求排列的个数
    求的是排列,所以是指数型生成函数。在[1,m]的范围内,相当于有m个因子,每个因子分别代表对1 ,2 ,3 ,4 , … ,m各自的限制。其中偶数有 f l o o r ( m 2 ) floor(\frac m2) floor(2m) 个,奇数有 m − f l o o r ( m 2 ) m-floor(\frac m2) mfloor(2m) ,设 t = f l o o r ( m 2 ) t=floor(\frac m2) t=floor(2m) ,可以得到指数型生成函数
    g ( x ) = ( 1 + x + x 2 2 ! + x 3 3 ! + …   ) m − t ( 1 + x 2 2 ! + x 4 4 ! + …   ) t g(x)=(1+x+\frac {x^2}{2!}+\frac {x^3}{3!}+\dots)^{m-t}(1+\frac {x^2}{2!}+\frac {x^4}{4!}+\dots)^{t} g(x)=(1+x+2!x2+3!x3+)mt(1+2!x2+4!x4+)t
    化简可得: g ( x ) = e x ( m − t ) ( e x + e − x 2 ) t = e x ( m − t ) ∑ i = 0 t C t i e x ( t − i ) e − x i 2 t = ∑ i = 0 t C t i e ( m − 2 i ) x 2 t = ∑ n = 0 ∑ i = 0 t C t i ( m − 2 i ) n 2 t x n n ! g(x)=e^{x(m-t)}(\frac {e^x+e^{-x}}2)^t=\frac{e^{x(m-t)}\sum_{i=0}^tC_t^ie^{x(t-i)}e^{-xi}}{2^t}=\frac {\sum_{i=0}^tC_t^ie^{(m-2i)x}}{2^t}=\sum\limits_{n=0}\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t}\frac {x^n}{n!} g(x)=ex(mt)(2ex+ex)t=2tex(mt)i=0tCtiex(ti)exi=2ti=0tCtie(m2i)x=n=02ti=0tCti(m2i)nn!xn
    因此可以得到 a n = ∑ i = 0 t C t i ( m − 2 i ) n 2 t a_n=\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t} an=2ti=0tCti(m2i)n a n a_n an 就是答案
  8. 硬币兑换问题
    将n元钱全部兑换成1元和2元,有多少种兑换方法
    g ( x ) = ( 1 + x + x 2 + …   ) ( 1 + x 2 + x 4 + …   ) g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots) g(x)=(1+x+x2+)(1+x2+x4+)
  9. 组合选取问题
    一副三色纸牌共 32 张其中红黄蓝每种颜色的牌各 10 张编号分别是1,2,3,…,10;另有大小王各一张,编号均为 0 。从这副牌中取出若干张牌,然后按如下规则计算分值:每张编号为 k 的牌计为 2 k 2^k 2k 分若它们的分值之和等于 2004 就称这些牌为一个“好”牌组。试求“好”牌组的个数
    g ( x ) = ( 1 + x 2 0 ) 2 ( 1 + x 2 1 ) 3 … ( 1 + x 2 10 ) 3 g(x)=(1+x^{2^0})^2(1+x^{2^1})^3\dots(1+x^{2^{10}})^3 g(x)=(1+x20)2(1+x21)3(1+x210)3
  10. 有 n 个标号为 1 , 2 , ⋯ , n 的球,放到 m 个无标号盒子 (盒内顺序有标号),且每个盒子球数不超过 k ,求方案数对 998244353 取模。
    考虑每个盒子内球的生成函数 ∑ i = 1 k x i \sum\limits_{i = 1} ^ {k}x ^ i i=1kxi ,那么 m 个盒子的生成函数就为 ( ∑ i = 1 k x i ) m \left( \sum\limits_{i = 1} ^ {k}x ^ i\right) ^ m (i=1kxi)m,那么方案数就为第 n 项系数。由于球带标号,所以需要对答案全排列,也就是乘 n! ,又由于盒子不带标号,所以要对答案除 m! ,那么答案为 n ! m ! × [ x n ] ( ∑ i = 1 k x i ) m \frac{n!}{m!} \times [x ^ n]\left( \sum\limits_{i = 1} ^ {k}x ^ i\right) ^ m m!n!×[xn](i=1kxi)m

1 ( 1 − x ) n = ∑ k = 0 ∞ C n + k − 1 k x k \frac{1}{(1-x)^n}=\sum\limits_{k=0}^\infty C_{n+k-1}^kx^k (1x)n1=k=0Cn+k1kxk
1 1 − x = 1 + x + x 2 + x 3 + . . . \frac{1}{1-x}=1+x+x^2+x^3+... 1x1=1+x+x2+x3+...
1 1 − x 6 = 1 + x 6 + x 12 + . . . \frac{1}{1-x^6}=1+x^6+x^{12}+... 1x61=1+x6+x12+...
1 − x 6 1 − x = 1 + x + x 2 + x 3 + x 4 + x 5 \frac{1-x^6}{1-x}=1+x+x^2+x^3+x^4+x^5 1x1x6=1+x+x2+x3+x4+x5
x ( 1 − x ) 2 = x + 2 x 2 + 3 x 3 + . . . \frac{x}{(1-x)^2}=x+2x^2+3x^3+... (1x)2x=x+2x2+3x3+...
e x = 1 + 1 1 ! x + 1 2 ! x 2 + 1 3 ! x 3 + . . . e^x=1+\frac{1}{1!}x+\frac{1}{2!}x^2+\frac{1}{3!}x^3+... ex=1+1!1x+2!1x2+3!1x3+...
e 2 x = 1 + 1 1 ! 2 x + 1 2 ! ( 2 x ) 2 + 1 3 ! ( 2 x ) 3 + . . . e^{2x}=1+\frac{1}{1!}2x+\frac{1}{2!}(2x)^2+\frac{1}{3!}(2x)^3+... e2x=1+1!12x+2!1(2x)2+3!1(2x)3+...

53.康托展开

P5367 【模板】康托展开
对于一个1到n的排列{a1​ ,a2​ ,⋯,an​ },比它小的排列有 ∑ i = 1 n s u m a i × ( n − i ) ! \sum\limits_{i=1}^nsum_{a_i}\times(n-i)! i=1nsumai×(ni)!
s u m a i sum_{a_i} sumai为在 ai 后面的元素里比它小的元素个数,即 ∑ j = 1 n ( a j ​ < a i ) \sum\limits_{j=1}^n(a_j​<a_i) j=1n(aj<ai),树状数组求解
逆康托展开
类似于进制转换,不断 %(n-i)! ,/(n-i)!就可以得到sum数组,然后就可以还原出原排列。

54.巴什博弈

HDU - 1846 Brave Game
各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;
如果游戏的双方使用的都是最优策略,请输出哪个人能赢。

结论:被(m+1)整除,先手败

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
void solve() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        int x, y;
        cin >> x >> y;
        if (x % (y + 1)==0) cout << "second\n";
        else cout << "first\n";
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    _t = 1;
    //cin >> _t;
    while (_t--) {
        solve();
    }
    return 0;
}

55.nim博弈

P2197 【模板】nim 游戏
甲,乙两个人玩 nim 取石子游戏。
nim 游戏的规则是这样的:地上有 n n n 堆石子(每堆石子数量小于 1 0 4 10^4 104),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 n n n 堆石子的数量,他想知道是否存在先手必胜的策略。

结论:异或和为0,先手败

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
void solve() {
    int ans = 0, n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        int a;
        cin >> a;
        ans ^= a;
    }
    if (ans) cout << "Yes\n";
    else cout << "No\n";
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    //_t = 1;
    cin>>_t;
    while (_t--) {
        solve();
    }
    return 0;
}

56.阶梯博弈

AcWing 892. 台阶-Nim游戏
现在,有一个n级台阶的楼梯,每级台阶上都有若干个石子,其中第i级台阶上有 a i a_i ai个石子(i≥1)。
两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。
已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。

结论:当奇数级阶梯的石子数量异或和为0,先手必败,否则先手必胜。

57.威佐夫博弈

P2252 [SHOI2002]取石子游戏|【模板】威佐夫博弈
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

结论:等于(b-a)*1.618,先手败

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
void solve() {
    int a, b;
    cin >> a >> b;
    double temp = (sqrt(5) + 1) / 2;
    if (a > b) swap(a, b);
    if (a == int((b - a) * temp)) cout << 0;
    else cout << 1;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    _t = 1;
    //cin >> _t;
    while (_t--) {
        solve();
    }
    return 0;
}

58.斐波那契博弈

有一堆个数为n(n>=2)的石子,游戏双方轮流取石子,规则如下:
1)先手不能在第一次把所有的石子取完,至少取1颗;
2)之后每次可以取的石子数至少为1,至多为对手刚取的石子数的2倍。
约定取走最后一个石子的人为赢家,求必败态。

结论:当n为斐波那契数时,先手必败。

59.P-position与N-positon

P-position为前一个玩家的必胜位置,N-position为下一个玩家的必胜位置

60.图游戏与SG函数

sg(x)等于x所有后缀sg值中未出现过的最小非负整数
SG函数实现巴什博弈,sg(x)=0的点是必败点

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e3 + 10;
int n, m, sg[N], s[N];
void getsg() {
    memset(sg, 0, sizeof(sg));
    for (int i = 1; i <= n; ++i) {
        memset(s, 0, sizeof(s));
        for (int j = 1; j <= m && i >= j; ++j) s[sg[i - j]] = 1;
        for (int j = 0; j <= n; ++j) {
            if (!s[j]) {
                sg[i] = j;
                break;
            }
        }
    }
}
void solve() {
    cin >> n >> m;
    getsg();
    if (sg[n]) cout << "first\n";
    else cout << "second\n";
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _t;
    //_t = 1;
    cin>>_t;
    while (_t--) {
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值