特辑:卡特兰数 & 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("
");
	}
};

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
", 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("
");
	}
};

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
", 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("
");
	}
};

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 imes (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
", 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
", 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
", 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
", 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
", 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