特辑:卡特兰数 & Prufer & BSGS

总共9道题怎么显示我A了10道(

Part 1: 卡特兰数:

Problem A:网格

不是卡特兰数,但推导过程类似。

首先A到B总步数为曼哈顿距离 $ m + n $ .于是总方案数为 $ C_{m + n} ^ m $.

考虑非法的情况,把所有跨越那条分界线的路径沿恰好跨过的那条线翻折,等价于从 \((0, 0)\)\((m - 1, n + 1)\),非法方案数为 $ C_{m + n} ^ {m - 1} $。

数太大啦,用质因数分解 + BigInt 求。

BigInt 真好用 x1

#include <bits/stdc++.h>

int n, m, mip[10005], pri[10005], cnt[10005][10005], fin[10005];

void init(int t) {
    for (int i = 2; i <= t; i++) {
        if (!mip[i]) mip[i] = i, pri[++pri[0]] = i;
        for (int j = 1; j <= pri[0] && i * pri[j] <= t; j++) {
            mip[i * pri[j]] = pri[j];
            if (i % pri[j] == 0) break;
        }
    }
    for (int i = 2; i <= t; i++) {
        for (int j = 1; j <= pri[0]; j++) {
            int x = i;
            while (x % pri[j] == 0) ++cnt[i][j], x /= pri[j];
            cnt[i][j] += cnt[i - 1][j];
        }
    }
}

struct BigInt {
    int l, a[23333], base;
    BigInt() {
        l = 0, base = 10;
        memset(a, 0, sizeof(a));
    }
    BigInt Trans(int x) {
        BigInt y;
        while (x)
            y.a[++y.l] = x % y.base, x /= y.base;
        return y;
    }
    friend BigInt operator +(BigInt x, BigInt y) {
        BigInt z;
        z.l = std::max(x.l, y.l);
        for (int i = 1; i <= z.l; i++)
            z.a[i] = x.a[i] + y.a[i], z.a[i + 1] += z.a[i] / x.base, x.a[i] %= x.base;
        if (z.a[z.l + 1]) z.l++;
        return z;
    }
    friend BigInt operator +(BigInt x, int y) {
        BigInt tmp = tmp.Trans(y);
        return x + tmp;
    }
    friend BigInt operator -(BigInt x, BigInt y) {
        BigInt z;
        z.l = std::max(x.l, y.l);
        for (int i = 1; i <= z.l; i++) {
            if (x.a[i] < y.a[i]) x.a[i] += x.base, x.a[i + 1]--;
            z.a[i] = x.a[i] - y.a[i];
        }
        while (!z.a[z.l] && z.l) z.l--;
        if (z.l == 0) z.a[1] = 1, z.l = 1;
        return z;
    }
    friend BigInt operator *(BigInt x, BigInt y) {
        BigInt z;
        z.l = x.l + y.l;
        if ((x.l == 1 && x.a[1] == 0) || (y.l == 1 && y.a[1] == 0)) {
            z.l = 1;
            return z;
        }
        for (int i = 1; i <= x.l; i++)
            for (int j = 1; j <= y.l; j++)
                z.a[i + j - 1] += x.a[i] * y.a[j], z.a[i + j] += z.a[i + j - 1] / x.base, z.a[i + j - 1] %= x.base;
        while (!z.a[z.l] && z.l) z.l--;
        if (!z.l) {z.l = 1, z.a[1] = 0;}
        return z;
    }
    friend BigInt operator *(BigInt x, int y) {
        BigInt z; int l = x.l;
        for (int i = 1; i <= l; i++)
            z.a[i] += x.a[i] * y, z.a[i + 1] += z.a[i] / x.base, z.a[i] %= x.base;
        while (z.a[l + 1])
            l++, z.a[l + 1] += z.a[l] / x.base, z.a[l] %= x.base;
        z.l = l;
        while (!z.a[z.l]) z.l--;
        return z;
    }
    friend BigInt operator /(BigInt x, int y) {
        BigInt z; z.l = x.l;
        int t = 0;
        for (int i = x.l; i >= 1; i--)
            t = t * 10 + x.a[i], z.a[i] = t / y, t %= y;
        while (!z.a[z.l]) z.l--;
        return z;
    }
    void print() {
        for (int i = l; i >= 1; i--)
            printf("%d", a[i]);
        printf("\n");
    }
};

BigInt Qpow(int x, int p) {
    BigInt ret = ret.Trans(1), tmp = tmp.Trans(x);
    for (; p; p >>= 1, tmp = tmp * tmp)
        if (p & 1) ret = ret * tmp;
    return ret;
}

signed main() {
    scanf("%d%d", &n, &m);
    init(n+ m);
    for (int i = 1; i <= pri[0]; i++)
        fin[i] = cnt[n + m][i] + cnt[n - m + 1][i] - cnt[n - m][i] - cnt[m][i] - cnt[n + 1][i];
    BigInt ans; ans.l = 1, ans.a[1] = 1;
    for (int i = 1; i <= pri[0]; i++)
        ans = ans * Qpow(pri[i], fin[i]);
    ans.print();
    return 0;
}

Problem B: 有趣的数列

和出栈入栈没区别,裸卡特兰数。

数太大啦,用质因数分解

能取模,不用BigInt了(

#include <bits/stdc++.h>
#define ll long long

ll ans = 1;
int n, p, pri[10000005], mip[10000005], cnt[10000005];

ll Qpow(int x, int b, int p) {
    ll ret = 1;
    for (; b; b >>= 1, x = x * x % p)
        if (b & 1) ret = ret * x % p;
    return ret;
}

signed main() {
    scanf("%d%d", &n, &p);
    for (int i = 2; i <= n; i++) cnt[i] = -1;
    for (int i = n + 2; i <= 2 * n; i++) cnt[i] = 1;
    for (int i = 2; i <= 2 * n; i++) {
        if (!mip[i]) mip[i] = i, pri[++pri[0]] = i;
        for (int j = 1; j <= pri[0] && pri[j] * i <= 2 * n; j++) {
            mip[pri[j] * i] = pri[j];
            if (i % pri[j] == 0) break;
        } 
    }
    for (int i = 2 * n; i >= 2; i--) {
        if (mip[i] != i) {
            cnt[mip[i]] += cnt[i];
            cnt[i / mip[i]] += cnt[i];
        } else ans = (ans * Qpow(i, cnt[i], p)) % p;
    }
    printf("%lld\n", ans);
    return 0;
}

Problem C: 树屋阶梯

没那么裸的卡特兰数

n阶阶梯分成n块,那么每一块必定包含一个"突起"。

而我们关注包含左下角底角那块,它的宽度可能为1 - n.而它可以把阶梯分成0阶和n-1阶,1阶和n-2阶.....

最后答案为:\(f(n) = f(0)f(n-1) + f(1)(n-2) + ...\),这玩意就是卡特兰数。

数太大啦,用质因...

不用,用递推的那个卡特兰数公式(

然而还是要BigInt

BigInt 真好用 x2

#include <bits/stdc++.h>

int n;

struct BigInt {
    int l, a[2333], base;
    BigInt() {
        l = 0, base = 10;
        memset(a, 0, sizeof(a));
    }
    friend BigInt operator +(BigInt x, BigInt y) {
        BigInt z;
        z.l = std::max(x.l, y.l);
        for (int i = 1; i <= z.l; i++)
            z.a[i] = x.a[i] + y.a[i], z.a[i + 1] += z.a[i] / x.base, x.a[i] %= x.base;
        if (z.a[z.l + 1]) z.l++;
        return z;
    }
    friend BigInt operator -(BigInt x, BigInt y) {
        BigInt z;
        z.l = std::max(x.l, y.l);
        for (int i = 1; i <= z.l; i++) {
            if (x.a[i] < y.a[i]) x.a[i] += x.base, x.a[i + 1]--;
            z.a[i] = x.a[i] - y.a[i];
        }
        while (!z.a[z.l] && z.l) z.l--;
        if (z.l == 0) z.a[1] = 1, z.l = 1;
        return z;
    }
    friend BigInt operator *(BigInt x, BigInt y) {
        BigInt z;
        z.l = x.l + y.l;
        if ((x.l == 1 && x.a[1] == 0) || (y.l == 1 && y.a[1] == 0)) {
            z.l = 1, z.a[1] = 0;
            return z;
        }
        for (int i = 1; i <= x.l; i++)
            for (int j = 1; j <= y.l; j++)
                z.a[i + j - 1] += x.a[i] * y.a[j], z.a[i + j] += z.a[i + j - 1] / x.base, z.a[i + j - 1] %= x.base;
        if (z.a[z.l + 1]) z.l++;
        return z;
    }
    friend BigInt operator *(BigInt x, int y) {
        BigInt z; int l = x.l;
        for (int i = 1; i <= l; i++)
            z.a[i] += x.a[i] * y, z.a[i + 1] += z.a[i] / x.base, z.a[i] %= x.base;
        while (z.a[l + 1])
            l++, z.a[l + 1] += z.a[l] / x.base, z.a[l] %= x.base;
        z.l = l;
        return z;
    }
    friend BigInt operator /(BigInt x, int y) {
        BigInt z; z.l = x.l;
        int t = 0;
        for (int i = x.l; i >= 1; i--)
            t = t * 10 + x.a[i], z.a[i] = t / y, t %= y;
        while (!z.a[z.l]) z.l--;
        return z;
    }
    void print() {
        for (int i = l; i >= 1; i--)
            printf("%d", a[i]);
        printf("\n");
    }
};

signed main() {
    scanf("%d", &n);
    BigInt Cat[505];
    Cat[1].l = 1, Cat[1].a[1] = 1;
    for (int i = 2; i <= n; i++)
        Cat[i] = Cat[i - 1] * (4 * i - 2) / (i + 1);
    Cat[n].print();
    return 0;
}

Part 2: Prufer序列

Problem D: 树的计数

根据Prufer序列的性质,树节点数为n,Prufer序列长为n - 2,每个点出现度数-1次。

所以数的种数为\(C_{n - 2} ^ {d_1 - 1} C_{n - 2 - (d_1 - 1)} ^ {d_2 - 1} ...\) ,化简得:\(\frac{(n - 2) !}{\prod \limits_{i = 1}^{n}(d_i - 1)!}\).

数不大了,不用BigInt啦

但分解质因数还是要的(

#include <bits/stdc++.h>
#define ll long long

int n, d[155], sum, pri[500], mip[500], cnt[500][500], ans[500];
ll QAQ;

ll Qpow(int x, int b) {
    ll ret = 1;
    for (; b; b >>= 1, x *= x)
        if (b & 1) ret *= x;
    return ret;
}

signed main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", d + i);
        if (d[i] == 0 && n > 1) {
            puts("0");
            return 0;
        }
        sum += d[i] - 1;
    }
    if (sum != n - 2) {
        puts("0");
        return 0;
    }
    for (int i = 2; i <= n; i++) {
        if (mip[i] != i) mip[i] = i, pri[++pri[0]] = i;
        for (int j = 1; j <= pri[0] && i * pri[j] <= n; j++) {
            mip[i * pri[j]] = pri[j];
            if (i % pri[j] == 0) break;
        }
    }
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= pri[0]; j++) {
            cnt[i][j] = 0;
            int x = i;
            while (x % pri[j] == 0) cnt[i][j]++, x /= pri[j];
            cnt[i][j] += cnt[i - 1][j];
        }
    }
    for (int i = 1; i <= pri[0]; i++) ans[i] = cnt[n - 2][i];
    QAQ = 1;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= pri[0]; j++)
            ans[j] -= cnt[d[i] - 1][j];
    for (int i = 1; i <= pri[0]; i++)
        QAQ = QAQ * Qpow(pri[i], ans[i]);
    printf("%lld\n", QAQ);
    return 0;
}

Problem E: 明明的烦恼

首先给出度数k个点,当前Prufer序列总长\(sum = \sum \limits_{i = 1} ^ k (d_i - 1)\), 它对答案贡献为 \(C_{n - 2} ^ {sum} \frac{sum!}{\prod \limits_{i = 1}^k (d_i - 1)!}\).

剩下的点填满 $ n - sum - 2 $ 个空位,贡献为 $ (n - k) ^ {n - sum - 2} $.

最终答案:$ C_{n - 2} ^ {sum} \frac{sum!}{\prod \limits_{i = 1}^k (d_i - 1)!} (n - k) ^ {n - sum - 2} $.

化简:\(\frac{(n - 2)!}{ (n - sum - 2)! \prod \limits_{i = 1}^k (d_i - 1)!} (n - k) ^ {n - sum - 2}\)

数太大啦,用质因数分解 + BigInt 求。

BigInt 真好用 x3

#include <bits/stdc++.h>

int n, d[1005], QAQ, pri[1005], mip[1005], cnt[1005][1005], fin[1005], sum;

void init(int t) {
    for (int i = 2; i <= t; i++) {
        if (!mip[i]) mip[i] = i, pri[++pri[0]] = i;
        for (int j = 1; j <= pri[0] && i * pri[j] <= t; j++) {
            mip[i * pri[j]] = pri[j];
            if (i % pri[j] == 0) break;
        }
    }
    for (int i = 2; i <= t; i++) {
        for (int j = 1; j <= pri[0]; j++) {
            int x = i;
            while (x % pri[j] == 0) ++cnt[i][j], x /= pri[j];
            cnt[i][j] += cnt[i - 1][j];
        }
    }
}

struct BigInt {
    int l, a[23333], base;
    BigInt() {
        l = 0, base = 10;
        memset(a, 0, sizeof(a));
    }
    BigInt Trans(int x) {
        BigInt y;
        while (x)
            y.a[++y.l] = x % y.base, x /= y.base;
        return y;
    }
    friend BigInt operator +(BigInt x, BigInt y) {
        BigInt z;
        z.l = std::max(x.l, y.l);
        for (int i = 1; i <= z.l; i++)
            z.a[i] = x.a[i] + y.a[i], z.a[i + 1] += z.a[i] / x.base, x.a[i] %= x.base;
        if (z.a[z.l + 1]) z.l++;
        return z;
    }
    friend BigInt operator +(BigInt x, int y) {
        BigInt tmp = tmp.Trans(y);
        return x + tmp;
    }
    friend BigInt operator -(BigInt x, BigInt y) {
        BigInt z;
        z.l = std::max(x.l, y.l);
        for (int i = 1; i <= z.l; i++) {
            if (x.a[i] < y.a[i]) x.a[i] += x.base, x.a[i + 1]--;
            z.a[i] = x.a[i] - y.a[i];
        }
        while (!z.a[z.l] && z.l) z.l--;
        if (z.l == 0) z.a[1] = 1, z.l = 1;
        return z;
    }
    friend BigInt operator *(BigInt x, BigInt y) {
        BigInt z;
        z.l = x.l + y.l;
        if ((x.l == 1 && x.a[1] == 0) || (y.l == 1 && y.a[1] == 0)) {
            z.l = 1;
            return z;
        }
        for (int i = 1; i <= x.l; i++)
            for (int j = 1; j <= y.l; j++)
                z.a[i + j - 1] += x.a[i] * y.a[j], z.a[i + j] += z.a[i + j - 1] / x.base, z.a[i + j - 1] %= x.base;
        while (!z.a[z.l] && z.l) z.l--;
        if (!z.l) {z.l = 1, z.a[1] = 0;}
        return z;
    }
    friend BigInt operator *(BigInt x, int y) {
        BigInt z; int l = x.l;
        for (int i = 1; i <= l; i++)
            z.a[i] += x.a[i] * y, z.a[i + 1] += z.a[i] / x.base, z.a[i] %= x.base;
        while (z.a[l + 1])
            l++, z.a[l + 1] += z.a[l] / x.base, z.a[l] %= x.base;
        z.l = l;
        while (!z.a[z.l]) z.l--;
        return z;
    }
    friend BigInt operator /(BigInt x, int y) {
        BigInt z; z.l = x.l;
        int t = 0;
        for (int i = x.l; i >= 1; i--)
            t = t * 10 + x.a[i], z.a[i] = t / y, t %= y;
        while (!z.a[z.l]) z.l--;
        return z;
    }
    void print() {
        for (int i = l; i >= 1; i--)
            printf("%d", a[i]);
        printf("\n");
    }
};

BigInt Qpow(int x, int p) {
    BigInt ret = ret.Trans(1), tmp = tmp.Trans(x);
    for (; p; p >>= 1, tmp = tmp * tmp)
        if (p & 1) ret = ret * tmp;
    return ret;
}

signed main() {
    scanf("%d", &n);
    init(1000);
    for (int i = 1, x; i <= n; i++) {
        scanf("%d", &x);
        if (x != -1) d[++QAQ] = x, sum += x - 1;
    }
    BigInt ans = ans.Trans(1);
    ans = ans * Qpow(n - QAQ, n - 2 - sum);
    for (int i = 1; i <= pri[0]; i++) {
        fin[i] += cnt[n - 2][i] - cnt[n - sum - 2][i];
        for (int j = 1; j <= QAQ; j++)
            fin[i] -= cnt[d[j] - 1][i];
    }
    for (int i = 1; i <= pri[0]; i++)
        ans = ans * Qpow(pri[i], fin[i]);
    ans.print();
    return 0;
}

Part 3: BSGS 百事公司

Problem F: 随机数生成器

数列神题(

\(x_{i+1} = (ax_i + b) \mod p\),使用构造法,\(x_{i + 1} + \lambda = a(x_i + \lambda) \mod p\).

解得:\(\lambda = \frac{b}{a - 1}\).

则数列\({x_i}\)通项公式为\(x_i = (x_1 + \frac{b}{a - 1}) a^{i - 1}\).

\(x_i = t\)时,\((x_1 + \frac{b}{a - 1}) a^{i - 1} = t\),即:\(a_{i - 1} \equiv t \times (x_1 + \frac{b}{a - 1}) ^ {-1} \mod p\).

这是BSGS的形式,根据这玩意解就万事了。

关于BSGS 拔山盖世,它用于求解 \(a^x \equiv b \mod p\) 这样的同余方程。

主要思路:把 \(x\) 拆成 \(i * m - j\), \(a^{i * m} \equiv b * a ^ {j} \mod p\).

从0 - m 枚举j算出右式的值塞到Hash Table,由于拆成减的形式,j越大越优。

最后枚举i,求左式,在Hash Table找对应值,找到就是答案,最后找不到无解。

因为我很懒,天天用map

#include <bits/stdc++.h>

long long p, a, b, x_1, t, T, tt;

long long Qpow(long long x, long long y, long long p) {
    long long ret = 1;
    for (; y; y >>= 1, x = x * x % p)
        if (y & 1) ret = ret * x % p;
    return ret;
}

long long BSGS(long long x, long long y, long long p) {
    long long m = ceil(sqrt(p)), t = 1;
    std::map<long long, long long> hash;
    for (int i = 0; i <= m; i++, y = y * x % p) hash[y] = i;
    long long f = Qpow(a, m, p);
    for (int i = 1; i <= m; i++) {
        t = t * f % p;
        if (hash.count(t)) return i * m - hash[t] + 1;
    }
    return -1;
}

signed main() {
    scanf("%lld", &T);
    while (T--) {
        scanf("%lld%lld%lld%lld%lld", &p, &a, &b, &x_1, &t);
        if (x_1 == t) {puts("1"); continue;}
        if (a == 0) {puts(b == t ? "2" : "-1"); continue;}
        if (a == 1) {
            if (b == 0) {puts("-1"); continue;}
            long long c = Qpow(b, p - 2, p) % p, d = (t - x_1 + p) % p;
            printf("%lld\n", d * c % p + 1);
            continue;
        }
        long long u = b * Qpow(a - 1, p - 2, p) % p;
        tt = ((t + u) * Qpow((x_1 + u) % p, p - 2, p)) % p;
        printf("%lld\n", BSGS(a, tt, p));
    }
    return 0;
}

Problem G: 计算器

模板三合一,不说了,秒了

#include <bits/stdc++.h>
#define ll long long

int T, opt;
ll y, z, p;

ll Qpow(ll x, int b, ll p) {
    ll ret = 1;
    for (; b; b >>= 1, x = x * x % p)
        if (b & 1) ret = ret * x % p;
    return ret;
} 

ll BSGS(ll y, ll z, ll p) {
    std::map<ll, ll> hash;
    ll m = ceil(sqrt(p)), t = 1;
    for (int i = 0; i <= m; i++, z = z * y % p) hash[z] = i;
    ll s = Qpow(y, m, p);
    for (int i = 1; i <= m; i++)
        if (t = t * s % p, hash.count(t)) return i * m - hash[t];
    return -1;
}

signed main() {
    scanf("%d%d", &T, &opt);
    while (T--) {
        scanf("%lld%lld%lld", &y, &z, &p);
        ll ans = 0;
        if (opt == 1) ans = Qpow(y, z, p) % p;
        else if (opt == 2) ans = (y % p != 0) ? z * Qpow(y, p - 2, p) % p : -1;
        else ans = BSGS(y % p, z % p, p);
        if (ans == -1 || (ans == 0 && opt == 3)) puts("Orz, I cannot find x!");
        else printf("%lld\n", ans);
    }
    return 0;
}

Problem H: Discrete Logging

真BSGS板子,秒了

#include <bits/stdc++.h>
#define ll long long

ll p, b, n;

ll Qpow(ll x, int b, ll p) {
    ll ret = 1;
    for (; b; b >>= 1, x = x * x % p)
        if (b & 1) ret = ret * x % p;
    return ret;
}

ll BSGS(ll a, ll n, ll p) {
    std::map<ll, ll> hash;
    ll m = ceil(sqrt(p)), t = 1;
    for (int i = 0; i <= m; i++, n = n * a % p) hash[n] = i;
    ll f = Qpow(a, m, p);
    for (int i = 1; i <= m; i++)
        if (t = t * f % p, hash.count(t)) return i * m - hash[t];
    return -1;
}

signed main() {
    while (scanf("%lld%lld%lld", &p, &b, &n) != EOF) {
        ll bsgs = BSGS(b, n, p);
        if (bsgs == -1) puts("no solution");
        else printf("%lld\n", bsgs);
    }
    return 0;
}

Problem I: Matrix

写个矩阵类,把乘法什么的封装起来。

仍然懒得写Hash Table,重载小于号塞map,其余照旧

秒了

#include <bits/stdc++.h>

int n, p;

struct Matrix {
    int a[75][75];
    friend Matrix operator *(Matrix x, Matrix y) {
        Matrix z = {};
        for (int k = 1; k <= n; k++)
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    z.a[i][j] = (z.a[i][j] + x.a[i][k] * y.a[k][j] % p) % p;
        return z;
    }
    void read(int x) {
        for (int i = 1; i <= x; i++) 
            for (int j = 1; j <= x; j++)
                scanf("%d", &a[i][j]);
    }
    friend bool operator <(Matrix x, Matrix y) {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                if (x.a[i][j] != y.a[i][j]) return x.a[i][j] < y.a[i][j];
        return 0;
    }
} A, B;

void BSGS() {
    std::map<Matrix, int> hash;
    int m = ceil(sqrt(p));
    for (int i = 0; i <= m; i++, B = B * A) hash[B] = i;
    Matrix f = A;
    for (int i = 1; i <= m - 1; i++) f = f * A;
    Matrix t = f;
    for (int i = 1; i <= m; i++, t = t * f)
        if (hash.count(t)) printf("%d\n", i * m - hash[t]), exit(0);
}

signed main() {
    scanf("%d%d", &n, &p);
    A.read(n), B.read(n);
    BSGS(); 
    return 0;
}

转载于:https://www.cnblogs.com/gekoo/p/11233875.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值