二分图+数学

二分图&数学

一、二分图

定义:二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集( i ∈ A i \in A iA , j ∈ B j \in B jB ),则称图G为一个二分图。不存在奇数环的无向图。
二分图判定:染色法
实现方式:以任意一点为起点进行染色,dfs遍历所有点,相邻的点染不同颜色,相隔一个点的两染相同颜色,边遍历边判定是否有冲突,(即判定相邻的两点是否为同一颜色,)如果有冲突则不存在二分图。
#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int h[N], e[N], ne[N], idx;
int color[N];

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

bool dfs(int u, int c) {
    color[u] = c;
    for (int i = h[u]; i != -1; i = ne[i]) {
        if (!color[e[i]]) {
            if (!dfs(e[i], 3 - c)) return false;
        }
        else if (color[e[i]] == c) return false;
    }
    return true;
}

int main() {
    memset(h, -1, sizeof(h));
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        int a, b;
        scanf("%d %d", &a, &b);
        add(a, b); add(b, a);
    }
    for (int i = 1; i <= n; i++) {
        if (!color[i]) {
            if (!dfs(i, 1)) {
                printf("No\n");
                return 0;
            }
        }
    }
    printf("Yes\n");
    return 0;
}

二、二分图的最大匹配

匹配方法:匈牙利算法(月老牵线)
实现方式:枚举一边(男生)去匹配另一边(找女生),如果可以匹配成功则进行标记(牵红线),如果不成功,找到那个没能成功匹配的那个点已经匹配的点(找到那个女生的现任男朋友),查找那个点有没有其他可以匹配的点可供替换(找那个现任男朋友的备胎),有则有希望,可以尝试让那个点重新匹配(挖那个现任男朋友的墙角),使其重新匹配一个点(让他换一个女朋友)。
代码实现:
#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

const int N = 2e5 + 10;

int n1, n2, m;
int h[N], e[N], ne[N], idx;
int match[N];
bool st[N];
int ans;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool find(int x) {
    for (int i = h[x]; i != -1; i = ne[i]) {
        if (!st[e[i]]) {
            st[e[i]] = true;
            if (match[e[i]] == 0 || find(match[e[i]])) {
                match[e[i]] = x;
                return true;
            }
        }
    }
    return false;
}

int main() {
    scanf("%d %d %d", &n1, &n2, &m);
    memset(h, -1, sizeof(h));
    for (int i = 0; i < m; i++) {
        int a, b;
        scanf("%d %d", &a, &b);
        add(a, b);
    }
    for (int i = 1; i <= n1; i++) {
        memset(st, false, sizeof(st));
        if (find(i)) ans++;
    }
    printf("%d\n", ans);
    return 0;
}

三、欧拉函数 Φ \Phi Φ

ϕ ( n ) \phi(n) ϕ(n):表示 1 − n 1-n 1n 中与 n n n 互质的数的个数

1.公式:

N = p 1 α 1 ∗ p 2 α 2 ∗ p 3 α 3 ∗ . . . ∗ p k α k N = p_1^{\alpha_1} * p_2^{\alpha_2} * p_3^{\alpha_3} *...* p_k^{\alpha_k} N=p1α1p2α2p3α3...pkαk

ϕ ( N ) = N ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) ∗ ( 1 − 1 p 3 ) ∗ . . . ∗ ( 1 − 1 p k ) \phi(N) = N * (1 - \cfrac{1}{p_1}) * (1 - \cfrac{1}{p_2}) * (1 - \cfrac{1}{p_3}) *...* (1 - \cfrac{1}{p_k}) ϕ(N)=N(1p11)(1p21)(1p31)...(1pk1)

2.证明:(容斥原理)

1.从 1 − n 1-n 1n 中去掉所有 p 1 , p 2 , p 3 , . . . , p k p_1, p _2, p_3,..., p_k p1,p2,p3,...,pk 的倍数;
2.加上所有 p i ∗ p j p_i * p_j pipj 的倍数数;
3.去掉所以 p i ∗ p j ∗ p k p_i * p_j * p_k pipjpk 的倍数;
3.加上所有 p i ∗ p j ∗ p k ∗ p l p_i * p_j * p_k * p_l pipjpkpl 的倍数
.
.
.
n…
所以:
N N N − N p 1 − N p 2 − N p 3 − . . . − N p k - \cfrac{N}{p_1} - \cfrac{N}{p_2} - \cfrac{N}{p_3} -...- \cfrac{N}{p_k} p1Np2Np3N...pkN + N p 1 ∗ p 2 + N p 2 ∗ p 3 + N p 3 ∗ p 4 + . . . + N p k − 1 ∗ p k + \cfrac{N}{p_1 * p_2} + \cfrac{N}{p_2 * p_3} + \cfrac{N}{p_3 * p_4} +...+ \cfrac{N}{p_{k-1} * p_k} +p1p2N+p2p3N+p3p4N+...+pk1pkN − . . . -... ... + ( − 1 ) k ∗ N p 1 ∗ p 2 ∗ p 3 ∗ . . . ∗ p k + (-1)^k * \cfrac{N}{p_1 * p_2 * p_3 *...* p_k} +(1)kp1p2p3...pkN

= N ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) ∗ ( 1 − 1 p 3 ) ∗ . . . ∗ ( 1 − 1 p k ) = N * (1 - \cfrac{1}{p_1}) * (1 - \cfrac{1}{p_2}) * (1 - \cfrac{1}{p_3}) *...* (1 - \cfrac{1}{p_k}) =N(1p11)(1p21)(1p31)...(1pk1)

代码实现:
#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        int a, ans;
        scanf("%d", &a);
        ans = a;
        for (int i = 2; i <= a / i; i++) {
            if (a % i == 0) {
                ans = ans / i * (i - 1);
                while (a % i == 0) a /= i;
            }
        }
        if (a > 1) ans = ans / a * (a - 1);
        printf("%d\n", ans);
    }
    return 0;
}
3.筛法求欧拉函数
原理:

因为: p r i m e s < = n / i primes <= n / i primes<=n/i
然后:
情况 1:     i % primes == 0

有: ϕ ( p r i m e s ∗ i ) = p r i m e s ∗ ϕ ( i ) \phi(primes * i) = primes * \phi(i) ϕ(primesi)=primesϕ(i)

情况 2:     i % primes != 0

有 : ϕ ( p r i m e s ∗ i ) = ( p r i m e s − 1 ) ∗ ϕ ( i ) \phi(primes * i) = (primes - 1)* \phi(i) ϕ(primesi)=(primes1)ϕ(i)

推导:联系“公式”易证
代码实现:
#include <iostream>
#include <cstdio>

using namespace std;

const int N = 1e6 + 10;

int n;
int primes[N], cnt;
int phi[N];
bool st[N];

long long get_eulers(int x) {
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            primes[++cnt] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; primes[j] <= x / i; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) {
                phi[primes[j] * i] = primes[j] * phi[i];
                break;
            } 
            phi[primes[j] * i] = (primes[j] - 1) * phi[i];
        }
    }
    long long ans = 0;
    for (int i = 1; i <= x; i++) 
        ans += phi[i];
    return ans;
}
int main() {
    scanf("%d", &n);
    printf("%lld\n", get_eulers(n));
    return 0;
}

四、欧拉定理

a a a n n n 互质,则有: a ϕ ( n ) ≡ 1 a^{\phi(n)} \equiv 1 aϕ(n)1 ( m o d (mod (mod n ) n) n)
证明:

假设: 1 − n 1 - n 1n 中, 与 n n n 互质的数有: a 1 , a 2 , a 3 , . . . , a ϕ ( n ) a_1, a_2, a_3,..., a_{\phi(n)} a1,a2,a3,...,aϕ(n)。①
由于: g c d ( a , n ) = = 1 gcd(a, n) == 1 gcd(a,n)==1
所以: a ∗ a 1 , a ∗ a 2 , a ∗ a 3 , . . . , a ∗ a ϕ ( n ) a * a_1, a * a_2, a * a_3,..., a * a_{\phi(n)} aa1,aa2,aa3,...,aaϕ(n) 也与 n n n 互质。②
由于 1 − n 1 - n 1n 中, 与 n n n 互质的数有只有 ϕ ( n ) \phi(n) ϕ(n) 个,所以 ①,②两组数在 m o d mod mod n n n 的情况下是同一组数。
所以有: a 1 ∗ a 2 ∗ a 3 ∗ . . . ∗ a ϕ ( n ) ≡ a ϕ ( n ) ∗ ( a 1 ∗ a 2 ∗ a 3 ∗ . . . ∗ a ϕ ( n ) ) a_1 * a_2 * a_3 *...* a_{\phi(n)} \equiv a^{\phi(n)} * (a_1 * a_2 * a_3 *...* a_{\phi(n)}) a1a2a3...aϕ(n)aϕ(n)(a1a2a3...aϕ(n)) ( m o d (mod (mod n ) n) n)

两边消去 a 1 ∗ a 2 ∗ a 3 ∗ . . . ∗ a ϕ ( n ) a_1 * a_2 * a_3 *...* a_{\phi(n)} a1a2a3...aϕ(n)

得: a ϕ ( n ) ≡ 1 a_{\phi(n)} \equiv 1 aϕ(n)1 ( m o d (mod (mod n ) n) n)

证毕。

费马定理: a n − 1 ≡ 1 a^{n - 1} \equiv 1 an11 ( m o d (mod (mod n ) n) n)

五、快速幂:

求: a b a^b ab m o d mod mod p p p
long long quick_mi(long long a, long long b, long long p) {
    long long ans = 1, tmp = a;
    while (b) {
        if (b & 1) ans = ans * tmp % p;
        tmp = tmp * tmp % p;
        b >>= 1;
    }
    return ans;
}
快速幂求逆元
逆元:由于 a b \cfrac{a}{b} ba m o d mod mod p p p ≠ \neq = a m o d p b m o d p \cfrac{amodp}{bmodp} bmodpamodp ,对于大整数 a a a, b b b, 不能处理,所以需要有一个数x,使得 a b \cfrac{a}{b} ba m o d mod mod p p p = = = a ∗ x a * x ax m o d mod mod p p p。其中 x x x 即为 b b b 的逆元。

求逆元最常用的方法有两种:1.快速幂求逆元;2.扩展欧几里得算法求逆元。这里是快速幂求逆元,下面会有用扩展欧几里得算法求逆元。

推导:

a b \cfrac{a}{b} ba ≡ \equiv a ∗ x a * x ax ( m o d (mod (mod p ) p) p)

1 b \cfrac{1}{b} b1 ≡ \equiv x x x ( m o d (mod (mod p ) p) p)

b ∗ x b * x bx ≡ \equiv 1 1 1 ( m o d (mod (mod p ) p) p)

由于 p p p 为质数, 所以有 b p − 1 ≡ 1 b^{p - 1} \equiv 1 bp11 ( m o d (mod (mod p ) p) p),

所以, x = b p − 2 x = b^{p-2} x=bp2

#include <cstdio>
#include <iostream>

using namespace std;

int t;
long long a, p;

long long quick_mi(long long a, long long p) {
    long long ans = 1, tmp = a, res = p - 2;
    while (res) {
        if (res & 1) ans = ans * tmp % p;
        tmp = tmp * tmp % p;
        res >>= 1;
    }
    return ans;
}

int main() {
    scanf("%d", &t);
    for (int i = 0; i < t; i++) {
        scanf("%lld %lld", &a, &p);
        if (a % p == 0) printf("impossible\n");
        else printf("%lld\n", quick_mi(a, p));
    }
    return 0;
}

六、扩展欧几里得算法

裴蜀定理:对于一对任意的正整数 a , b a, b a,b, 一定存在整数 x , y x, y x,y, 使得 a ∗ x + b ∗ y = ( a , b ) a * x + b * y = (a, b) ax+by=(a,b)
代码实现:
void exgcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1, y = 0;
        return;
    }
    exgcd(b, a % b, y, x);
    y -= a / b * x;
}

a ∗ x + b ∗ y = ( a , b ) a * x + b * y = (a, b) ax+by=(a,b)

a a a % b ∗ x + b ∗ y = ( a , b ) b * x + b * y = (a, b) bx+by=(a,b)

因为: a a a % b b b = = = a − a / b ∗ b a - a / b * b aa/bb

所以: a ∗ x + b ∗ ( y − a / b ∗ x ) = ( a , b ) a * x + b * (y - a / b * x) = (a, b) ax+b(ya/bx)=(a,b)

所以一轮递归需要处理的只有 y = y − a / b ∗ x y = y - a / b * x y=ya/bx

扩展欧几里得算法求逆元:
#include <iostream>
#include <cstdio>
using namespace std;

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

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 0; i < t; i++) {
        long long a, p, x, y;
        scanf("%lld %lld", &a, &p);
        if (a % p == 0) printf("impossible\n");
        else 
        {
            exgcd(a, p, x, y);
            printf("%lld\n", (x % p + p) % p);
        }
    }
}

七、小结

数学写题学思路,数学巩固推公式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值