容斥与简单反演乱写

#define TBD ToBeDone 😅

容斥的本质

构造一组数\(f_i\),使得 \(\sum\limits_{i = 0} ^ {h_x} \binom {h_x}{i} f_i = g_x\),其中\(g_x\)为希望\(x\)这个元素被统计的次数,\(f_i\)为容斥系数,\(h_i\)\(x\)超集的大小。

可以理解对于一个需要统计的元素\(x\)为枚举包含其集合\(s\)的大小\(i\),对于所有大小为\(i\)\(s\)集合将其中元素都统计\(f_i\)次,使得最后\(x\)被恰好统计\(x\)次。

对于常规的容斥题,有\(g_x = 1\),此时\(f_i = (-1) ^{(i - 1)}\),可以带入用二项式定理验证。

例题

联考题 装饰

sol

image

点击查看代码
#include <bits/stdc++.h>
#define int long long

const int MAXN = 65;
struct Edge {
    int to, nxt;
}E[MAXN << 1];
int head[MAXN], ecnt = -1;

void add(int u, int v) {
    E[++ecnt].to = v;
    E[ecnt].nxt = head[u];
    head[u] = ecnt;
}

void solve() {
    memset(head, -1, sizeof head);
    int n, m, k;
    std::cin >> n >> m >> k;
    struct Dsu {
        std::vector <int> fa;
        void init(int n) {
            fa.resize(n + 1);
            for (int i = 0; i <= n; i++) fa[i] = i;
        }
        int find(int x) {
            return fa[x] == x ? x : fa[x] = find(fa[x]);
        }
        void merge(int x, int y) {
            int X = find(x), Y = find(y);
            if (X == Y) return;
            fa[X] = Y;
        }
    }dsu;

    for (int i = 1; i < n; i++) {
        int u, v;
        std::cin >> u >> v;
        add(u, v); add(v, u);
    }

    std::vector <std::vector<int> > path(m);
    std::vector <int> ine(n + 1);

    auto dfs = [&](auto self, int u, int fa) -> void {
        for (int i = head[u]; i != -1; i = E[i].nxt) {
            int v = E[i].to;
            if (v == fa) continue;
            ine[v] = i ^ 1;
            self(self, v, u);
        }
    };

    for (int i = 0; i < m; i++) {
        int u, v;
        std::cin >> u >> v;
        dfs(dfs, u, -1);
        int now = v;
        while (now != u) {
            path[i].push_back(ine[now] / 2);
            now = E[ine[now]].to;
        }
    }

    int up = (1 << m) - 1;
    int ans = 0;
    for (int mask = 0; mask <= up; mask++) {
        dsu.init(n - 1);
        for (int i = 0; i < m; i++) {
            if (mask & (1 << i)) {
                for (const auto &x : path[i]) {
                    if (x == path[i][0]) continue;
                    dsu.merge(path[i][0], x);
                }
            }
        }
        int cnt = 0;
        for (int i = 0; i < n - 1; i++) {
            if (dsu.find(i) == i) cnt++;
        }
        const int MOD = 1e9 + 7;
        auto power = [&](int a, int b) {
            int ret = 1;
            for (; b; b >>= 1) {
                if (b & 1) ret = (1ll * ret * a) % MOD;
                a = (1ll * a * a) % MOD;
            }
            return ret;
        };
        int contr = power(k, cnt);
        if (__builtin_popcount(mask) & 1) ans -= contr, ans += MOD, ans %= MOD;
        else ans += contr, ans %= MOD;
    }

    std::cout << ans << "\n";
}

signed main() {
    freopen("decoration.in", "r", stdin);
    freopen("decoration.out", "w", stdout);

    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

[ARC101E] Ribbons on Tree

sol 三个月前做过,现在不会了,不想某位神仙两年前做过现在一眼秒了😅

比较navie的想法是设\(dp{u,x}\)\(u\)的子树中有\(x\)个点未被覆盖,需要子树外一个点向内某个点相连的方案数,直接类似树背包转移有 \(dp_{u,x + y - 2k} = \sum \limits_{v \in son(u), x +y > 0,k \leq min(x, y)} dp_{u, x} \times dp_{v,y} \times \binom{x}{k} \times \binom{y}{k} \times k!\)

系数过于复杂,不便于优化。看到每条边至少被覆盖一次考虑容斥。

考虑边集\(S\)中的边一次都没被覆盖过,这等价于删去这些边。此时整张图被分成了若干个联通块,显然每个联通块内部匹配的方案数是 \(g(n) = \prod \limits _{2k \leq n, k \in N^*}{2k - 1}\),注意\(n\)为奇数时\(g(n) = 0\)

数据范围无法枚举子集,考虑\(dp\),重定义\(dp_{u,x}\)为以\(u\)为根的子树联通块大小为\(x\)的方案数(考虑容斥系数)。那么有这条边断掉 \(dp_{u,x} \times dp_{v,y} \times{g(y)} \times -1 \rightarrow dp_{u,x}\) (断边集合大小加一),保留这条边 \(dp_{u,x} \times dp_{v,y} \rightarrow dp_{u, x +y}\)

最后答案即为 \(\sum \limits_{i = 1} ^n dp_{1, i} \times g(i)\)。复杂度为树形背包的 \(O(n ^2)\)

点击查看代码
#include <bits/stdc++.h>

void solve() {
    int n;
    std::cin >> n;
    std::vector <std::vector<int> > adj(n + 1);
    for (int i = 1; i < n; i++) {
        int u, v;
        std::cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    const int MOD = 1e9 + 7;
    std::vector <int> g(n + 1);
    g[0] = 1;
    for (int i = 2; i <= n; i += 2) g[i] = 1ll * g[i - 2] * (i - 1) % MOD; 

    std::vector <std::vector<int> > dp(n + 1);
    std::vector <int> siz(n + 1);
    for (int i = 0; i <= n; i++) dp[i].resize(n + 1);
    auto dfs = [&](auto self, int u, int fa) -> void {
        siz[u] = 1;
        dp[u][1] = 1;
        for (const auto &v : adj[u]) {
            if (v == fa) continue;
            self(self, v, u);
            std::vector <int> tmp(siz[u] + siz[v] + 1);
            for (int j = 1; j <= siz[u]; j++) {
                for (int k = 1; k <= siz[v]; k++) {
                    tmp[j] += -1ll * dp[u][j] * dp[v][k] % MOD * g[k] % MOD;
                    tmp[j] %= MOD; tmp[j] += MOD; tmp[j] %= MOD;
                    tmp[j + k] += 1ll * dp[u][j] * dp[v][k] % MOD;
                    tmp[j + k] %= MOD;
                }
            }
            siz[u] += siz[v];
            for (int i = 1; i <= siz[u]; i++) dp[u][i] = tmp[i];
        }
    };
    dfs(dfs, 1, 0);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += 1ll * dp[1][i] * g[i] % MOD;
        ans %= MOD;
    }

    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

子集反演

\(g(s) = \sum\limits_{t \subseteq s} f(t)\),那么有 \(f(s) = \sum \limits_{t \subseteq s} (-1) ^ {(|s| - |t|)}g(t)\)。(子集形式)

\(g(s) = \sum\limits_{s \subseteq t} f(t)\),那么有 \(f(s) = \sum \limits_{s \subseteq t} (-1) ^ {(|t| - |s|)}g(t)\)。(超集形式)

image

例题

[ZJOI2016] 小星星

TBD

[SHOI2016] 黑暗前的幻想乡
TBD

二项式反演

\(g_n = \sum\limits_{i = 0} ^ n \binom{n}{i} f_i\),那么有\(f_n = \sum\limits_{i = 0} ^ n (-1) ^ {(n - i)}\binom{n}{i} g_i\)

\(g_n = \sum\limits_{i = n} ^ m \binom{i}{n} f_i\),那么有\(f_n = \sum\limits_{i = n} ^ m (-1) ^ {(i - n)}\binom{i}{n} g_i\)

证明:咕咕咕。

发现两种形式左右都是对称的,且-1的次数都为\(|i - n|\)便于背诵

本质是容斥在大小相同的集合贡献相同的一种特殊形式。

一般设\(f_n\)为恰好取\(n\)个的值,\(g_n\)为钦定取\(n\)个的值,通常情况下后者比前者更好计算,就完成了从至少到恰好的转化

例题

Another Filling the Grid

sol:TBD

点击查看代码
#include <bits/stdc++.h>

void solve() {
    int n, k;
    std::cin >> n >> k;

    const int MOD = 1e9 + 7;
    std::vector <int> fac(n + 1), inv(n + 1);
    auto power = [&](int a, int b) {
        int ret = 1;
        for (; b; b >>= 1) {
            if (b & 1) ret = (1ll * ret * a) % MOD;
            a = (1ll * a * a) % MOD;
        }
        return ret;
    };
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = (1ll * fac[i - 1] * i) % MOD;
    inv[n] = power(fac[n], MOD - 2);
    for (int i = n - 1; i >= 0; i--) {
        inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
    }
    auto C = [&](int n, int m) {
        return 1ll * fac[n] * inv[n - m] % MOD * inv[m] % MOD;
    };

    auto g = [&](int i, int j) {
        int lim1 = n * (i + j) - i * j, lim2 = n * n - lim1;
        return 1ll * C(n, i) * C(n, j) % MOD * power(k - 1, lim1) % MOD * power(k, lim2) % MOD;
    };
    auto f = [&](int x, int y) {
        int ret = 0;
        for (int i = x; i <= n; i++) {
            for (int j = y; j <= n; j++) {
                int v = 1ll * C(i, x) * C(j, y) % MOD * g(i, j) % MOD;
                if ((i + j - x - y) & 1) ret -= v, ret %= MOD, ret += MOD, ret %= MOD;
                else ret += v, ret %= MOD;
            }
        }
        return ret;
    };

    std::cout << f(0, 0) << "\n";
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

[NOI Online #2 提高组] 游戏

sol:TBD

点击查看代码
#include <bits/stdc++.h>

const int MAXN = 5005;
const int MOD = 998244353;

int dp[MAXN][MAXN];

void solve() {
    int n, m;
    std::cin >> n;
    m = n / 2;
    std::string s;
    std::cin >> s;
    std::vector <int> col(n + 1);
    for (int i = 1; i <= n; i++) col[i] = s[i - 1] - '0';
    std::vector <std::vector<int> > adj(n + 1);
    for (int i = 1; i < n; i++) {
        int u, v;
        std::cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    auto power = [&](int a, int b) {
        int ret = 1;
        for (; b; b >>= 1) {
            if (b & 1) ret = (1ll * a * ret) % MOD;
            a = (1ll * a * a) % MOD;
        }
        return ret;
    };
    std::vector <int> fac(n + 1), inv(n + 1);
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = (1ll * fac[i - 1] * i) % MOD;
    inv[n] = power(fac[n], MOD - 2);
    for (int i = n - 1; i >= 0; i--) {
        inv[i] = (1ll * inv[i + 1] * (i + 1)) % MOD;
    }
    auto C = [&](int n, int m) {
        return 1ll * fac[n] * inv[n - m] % MOD * inv[m] % MOD;
    };

    std::vector <std::vector<int> > siz(n + 1);
    for (int i = 0; i <= n; i++) siz[i].resize(2); 
    auto dfs = [&](auto self, int u, int fa) -> void {
        siz[u][col[u]] = 1; dp[u][0] = 1;
        for (const auto &v : adj[u]) {
            if (v == fa) continue;
            self(self, v, u);
            int s1 = (siz[u][0] + siz[u][1]) / 2, s2 = (siz[v][0] + siz[v][1]) / 2, s3 = (siz[u][0] + siz[u][1] + siz[v][0] + siz[v][1]) / 2;
            std::vector <int> tmp(s3 + 1);
            for (int j = 0; j <= s1; j++) {
                for (int k = 0; k <= s2; k++) {
                    if (j + k > m) break;
                    tmp[j + k] += 1ll * dp[u][j] * dp[v][k] % MOD;
                    tmp[j + k] %= MOD;
                }
            }
            siz[u][0] += siz[v][0], siz[u][1] += siz[v][1];
            for (int i = 0; i <= s3; i++) dp[u][i] = tmp[i];
        }
        for (int i = siz[u][col[u] ^ 1]; i >= 0; i--) {
            dp[u][i + 1] += 1ll * dp[u][i] * (siz[u][col[u] ^ 1] - i) % MOD;
            dp[u][i + 1] %= MOD;
        }
    };
    dfs(dfs, 1, 0);

    auto g = [&](int x) {
        return 1ll * dp[1][x] * fac[m - x] % MOD;
    };
    auto f = [&](int x) {
        int res = 0;
        for (int i = x; i <= m; i++) {
            int v = 1ll * C(i, x) * g(i) % MOD;
            if ((i - x) & 1) res -= v, res %= MOD, res += MOD, res %= MOD;
            else res += v, res %= MOD;
        }
        return res;
    };

    for (int i = 0; i <= m; i++) {
        std::cout << f(i) << "\n";
    }
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

Positions in Permutations

sol:TBD

点击查看代码
#include <bits/stdc++.h>

const int MAXN = 1005;
int dp[MAXN][MAXN][2][2];

void solve() {
    int n, k;
    std::cin >> n >> k;

    #define ll long long
    const int MOD = 1e9 + 7;
    std::vector <int> fac(n + 1), inv(n + 1);
    auto power = [&](int a, int b) {
        int ret = 1;
        for (; b; b >>= 1) {
            if (b & 1) ret = (1ll * ret * a) % MOD;
            a = (1ll * a * a) % MOD;
        }
        return ret;
    };
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[n] = power(fac[n], MOD - 2);
    for (int i = n - 1; i >= 0; i--) {
        inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
    }
    auto C = [&](int n, int m) {
        return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
    };

    dp[1][0][0][0] = dp[1][1][0][1] = 1;
    for (int i = 2; i <= n; i++) {
        for (int j = 0; j <= i; j++) {
            //select i - 1
            if (j) {
                dp[i][j][0][0] += dp[i - 1][j - 1][0][0]; dp[i][j][0][0] %= MOD;
                dp[i][j][1][0] += dp[i - 1][j - 1][0][1]; dp[i][j][1][0] %= MOD;                
            }
            //select i + 1
            if (j) {
                dp[i][j][0][1] += dp[i - 1][j - 1][0][0]; dp[i][j][0][1] %= MOD;
                dp[i][j][0][1] += dp[i - 1][j - 1][1][0]; dp[i][j][0][1] %= MOD;
                dp[i][j][1][1] += dp[i - 1][j - 1][0][1]; dp[i][j][1][1] %= MOD;
                dp[i][j][1][1] += dp[i - 1][j - 1][1][1]; dp[i][j][1][1] %= MOD;    
            }
            //select nothing
            dp[i][j][0][0] += dp[i - 1][j][1][0]; dp[i][j][0][0] %= MOD;
            dp[i][j][0][0] += dp[i - 1][j][0][0]; dp[i][j][0][0] %= MOD;
            dp[i][j][1][0] += dp[i - 1][j][0][1]; dp[i][j][1][0] %= MOD;
            dp[i][j][1][0] += dp[i - 1][j][1][1]; dp[i][j][1][0] %= MOD;
        }
    }

    auto g = [&](int x) {
        return 1ll * ((dp[n][x][1][0] + dp[n][x][0][0]) % MOD) * fac[n - x] % MOD;
    };

    auto f = [&](int x) {
        int ret = 0;
        for (int i = x; i <= n; i++) {
            int v = 1ll * g(i) * C(i, x) % MOD;
            if ((i - x) & 1) ret -= v, ret %= MOD, ret += MOD, ret %= MOD;
            else ret += v, ret %= MOD;
        }
        return ret;
    };

    std::cout << f(k) << "\n";
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

Timber
sol:TBD

点击查看代码
#include <bits/stdc++.h>

void solve() {
    int n, m, k;
    std::cin >> n >> m >> k;

    const int MOD = 998244353;
    std::vector <int> fac(n + 1), inv(n + 1), pw(n + 1);
    auto power = [&](int a, int b) {
        int ret = 1;
        for (; b; b >>= 1) {
            if (b & 1) ret = (1ll * ret * a) % MOD;
            a = (1ll * a * a) % MOD;
        }
        return ret;
    };
    fac[0] = 1; pw[0] = 1;
    for (int i = 1; i <= n; i++) {
        fac[i] = 1ll * fac[i - 1] * i % MOD;
        pw[i] = 1ll * pw[i - 1] * 2 % MOD;
    } 
    inv[n] = power(fac[n], MOD - 2);
    for (int i = n - 1; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
    auto C = [&](int n, int m) {
        if (n < 0 || m < 0) return 0ll;
        if (m > n) return 0ll;
        return 1ll * fac[n] * inv[n - m] % MOD * inv[m] % MOD; 
    };

    #define ll long long
    int ans = 0;
    for (int i = 0; i <= m; i++) {
        ll tmp = (ll)n - 1ll * (m + i) * k;
        if (tmp < 0) continue;
        int v = 1ll * pw[m - i] * C(m, i) % MOD * C(n - (m + i) * k, m) % MOD; 
        if (i & 1) ans -= v, ans %= MOD, ans += MOD, ans %= MOD;
        else ans += v, ans %= MOD;
    }

    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

广义容斥(容斥系数)

本人认为二项式反演不等于广义容斥,相反,只是容斥中的一种可以简化的特殊情况,只是容斥系数的推导可以用到二项式反演。

\( \begin{aligned} & \sum\limits_{i = 0} ^ {h_x} \binom {h_x}{i} f_i = g_x \\ & f_x = \sum\limits_{i = 0}^{h_x} (-1) ^ {(h_x - i)}\binom{h_x}{i} g_i \end{aligned} \)

例题

[COCI2009-2010#6] XOR

sol

设一个三角形由 \(t\) 个给定的三角形覆盖,那么有 \(f_t = \sum\limits_{i = 0} ^ {t} (-1) ^ {(t - i)} \binom{t}{i} [2 \nmid t]\)

发现只有为\(i\)奇数时才有贡献,所以 \(f_t = (-1) ^ {(t - 1)}\sum\limits_{i = 0} ^ {t} \binom{t}{i} [2 \nmid t]\)

考虑用二项式定理相减化简,具体来说如下。

\((a + b) ^ n - (a - b) ^ n = \sum\limits_{i = 0} ^ {n} \binom{n}{i} a ^ {n - i}b^i - \sum\limits_{i = 0} ^ {n} (-1) ^ i \binom{n}{i}a^{n - i}b^i = 2 \times \sum\limits_{i = 0} ^ n [2 \nmid n]\binom{n}{i}a^{n - i}b^i\)

所以有 \(f_t = (-1) ^ {(t - 1)} \frac{(1 + 1) ^ t - (1 - 1) ^ t}{2} = (-2) ^ {(t - 1)}\)

然后枚举子集计算子集中三角形面积交乘上系数相加即可。

还有由于是等腰直角三角形,比较特殊,无需计算几何,初中知识即可。

点击查看代码
#include <bits/stdc++.h>

#define ll long long

struct Tri {
    int x, y, r;
    double size() {
        if (r < 0) return 0.0;
        return 1.0 * r * r / 2.0;
    }
    Tri(int X = 0, int Y = 0, int R = 0) {
        x = X; y = Y; r = R;
    }
    bool operator == (const Tri &t) const {
        return x == t.x && y == t.y && r == t.r;
    };
};

Tri merge(Tri x, Tri y) {
    int c1 = x.x + x.y + x.r, c2 = y.x + y.y + y.r;
    int mxx = std::max(x.x, y.x), mxy = std::max(x.y, y.y);
    return Tri(mxx, mxy, std::min(c1, c2) - mxx - mxy);
}

void solve() {
    int n;
    std::cin >> n;
    std::vector <Tri> a(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i].x >> a[i].y >> a[i].r;
    }

    std::vector <int> f(n + 1);
    f[1] = 1;
    for (int i = 2; i <= n; i++) {
        f[i] = -2 * f[i - 1];
    }

    double ans = 0;
    for (int mask = 1; mask < (1 << n); mask++) {
        int sz = __builtin_popcount(mask);
        Tri tr = Tri(0, 0 ,0);
        for (int i = 0; i < n; i++) {
            if (mask & (1 << i)) {
                if (tr == Tri(0, 0, 0)) tr = a[i];
                else tr = merge(tr, a[i]);
            }
        }
        double s = tr.size();
        if (s) {
            ans += 1.0 * f[sz] * s;
        }
    }

    std::cout << std::fixed << std::setprecision(1) << ans << "\n";
    // printf("%.1lf\n", ans);
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    int t = 1;
    while (t--) {
        solve();
    }

    return 0;
}

HDU-6314 Matrix

sol 没必要写了,如果写一遍多半会和别人的题解大幅度相似

image

点击查看代码
#include <bits/stdc++.h>

const int MAXN = 3005;
const int MOD = 998244353;

#define ll long long
int C[MAXN][MAXN], f1[MAXN], f2[MAXN], pw[MAXN * MAXN];

void Mod(int &x) {
    if (x >= MOD) x -= MOD;
    if (x < 0) x += MOD;
}

void Mod(ll &x) {
    if (x >= MOD) x -= MOD;
    if (x < 0) x += MOD;
}

void init() {
    for (int i = 0; i < MAXN; i++) {
        for (int j = 0; j <= i; j++) {
            if (!j) C[i][j] = 1;
            else C[i][j] = C[i - 1][j] + C[i - 1][j - 1], Mod(C[i][j]);
        }
    }
    pw[0] = 1;
    for (int i = 1; i < MAXN * MAXN; i++) pw[i] = pw[i - 1] + pw[i - 1], Mod(pw[i]);
}

void solve(int n, int m, int a, int b) {
    int t = std::max(n, m);
    for (int i = 0; i <= t; i++) {
        f1[i] = f2[i] = 0;
    }
    f1[a] = f2[b] = 1;
    for (int i = a + 1; i <= n; i++) {
        for (int j = a; j < i; j++) {
            f1[i] -= 1ll * C[i - 1][j - 1] * f1[j] % MOD;
            Mod(f1[i]);
        }
    }
    for (int i = b + 1; i <= m; i++) {
        for (int j = b; j < i; j++) {
            f2[i] -= 1ll * C[i - 1][j - 1] * f2[j] % MOD;
            Mod(f2[i]);
        }
    }
    
    ll ans = 0;
    for (int i = a; i <= n; i++) {
        for (int j = b; j <= m; j++) {
            ans += 1ll * f1[i] * f2[j] % MOD * C[n][i] % MOD * C[m][j] % MOD * pw[(n - i) * (m - j)] % MOD;
            Mod(ans);
        }
    }

    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    init();
    int n, m, a, b;
    while (std::cin >> n >> m >> a >> b) {
        solve(n, m, a, b);
    }

    return 0;
}

HDU5072 Coprime
TBD

莫比乌斯反演

link,这玩意儿严格来讲属于数论了,不在此展开。

posted @ 2023-10-30 19:30  Katyusha_Lzh  阅读(57)  评论(1)    收藏  举报