初三组合恒等式和二项式定理练习 题解

A. 多项式

推柿子:

\[\begin{aligned} &\sum\limits_{k = 0}^{n}b_{k}(x - t)^{k}\\ =&\sum\limits_{k = 0}^{n}b_{k}\sum\limits_{i = 0}^{k}\binom{k}{i}x^{i}(-t)^{k-i}\\ =&\sum\limits_{0 \leqslant i \leqslant k \leqslant n}\binom{k}{i}b_{k}(-t)^{k - i}x^{i} \end{aligned} \]

因为相同次数的项系数相同,所以:

\[a_{i} = \sum\limits_{k = i}^{n}\binom{k}{i}b_{k}(-t)^{k - i} \]

二项式反演算是二项式的必备技能很合理吧,所以反演一下就有:

\[b_{k} = \sum\limits_{i = k}^{n}\binom{i}{k}a_{i}t^{i - k} \]

什么,\(n\)\(m\) 都太大了?\(\binom{i}{k}\) 不好算?\(\binom{i}{k} = \binom{i}{i - k}\)

不想写高精度?去学 python 得了。

代码(我的高精度在主函数里面,但确实短):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
basic_string<ll> n, m, mul = {1}, ans, tmp, mu;
string s;
int pos, sub, t, x, r, len, now, p, a[3388];
void del0(basic_string<ll>& s) {
	while(!s.empty() && !s.back()) s.pop_back();
	if(s.empty()) s = {0};
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	a[0] = 1;
	for(int i = 1; i < 3388; ++i) a[i] = (a[i - 1] * 1234ll + 5678) % 3389;
	cin >> s;
	for(const auto& i : s) n.push_back(i - '0');
	cin >> t >> s;
	for(const auto& i : s) m.push_back(i - '0'), pos = (pos * 10 + m.back()) % 3388;
	sub = (n.back() + 10 - m.back()) % 10;
	reverse(n.begin(), n.end()), reverse(m.begin(), m.end());
	for(int moew = 0; moew <= sub; ++moew, pos = (pos + 1) % 3388) {
		tmp = {0};
		for(const auto& i : mul) tmp.back() += i * a[pos], r = tmp.back() / 10, tmp.back() %= 10, tmp.push_back(r);
		while(tmp.back() >= 10) r = tmp.back() / 10, tmp.back() %= 10, tmp.push_back(r);
		len = max(ans.length(), tmp.length()), ans.resize(len + 1), tmp.resize(len + 1);
		for(int i = 0; i < len; ++i) ans[i] += tmp[i], r = ans[i] / 10, ans[i] %= 10, ans[i + 1] += r;
		r = 1; for(auto& i : m) i += r, r = i / 10, i %= 10; m.push_back(r);
		mu = {0};
		for(const auto& i : m) mu.back() += i * t, r = mu.back() / 10, mu.back() %= 10, mu.push_back(r);
		while(mu.back() >= 10) r = mu.back() / 10, mu.back() %= 10, mu.push_back(r);
		tmp.assign(mul.length() + mu.length() + 5, 0), now = 0;
		for(const auto& i : mul) {
			p = now;
			for(const auto& j : mu) tmp[p] += i * j, r = tmp[p] / 10, tmp[p] %= 10, tmp[p + 1] += r, ++p;
			++now;
		}
		del0(tmp);
		while(tmp.back() >= 10) r = tmp.back() / 10, tmp.back() %= 10, tmp.push_back(r);
		reverse(tmp.begin(), tmp.end());
		r = 0, mul.clear();
		for(const auto& i : tmp) r = r * 10 + i, mul.push_back(r / (moew + 1)), r %= moew + 1;
		reverse(mul.begin(), mul.end());
	}
	del0(ans);
	reverse(ans.begin(), ans.end());
	for(const auto& i : ans) cout << i;
	return 0;
}

B. A Very Simple Problem

怎么办怎么办看不出来哪里有二项式?

于是我尝试拉插。然后失败了。

那就研究一下通项公式或者递推公式。

如何从第 \(i\) 项推出 \(i + 1\) 项?直接展开:

\[\begin{aligned} &(i + 1)^{x}x^{i + 1}\\ =&x\sum\limits_{j = 0}^{x}\binom{x}{j}i^{j}x^{i}\\ \end{aligned} \]

发现 \(x\) 的范围非常小!懒得继续推什么式子直接给丢进矩阵里加速,矩阵多开一个 \(sum\) 的位置即可。

矩阵大概长这样:

\[\begin{bmatrix} i^{0}x^{i} & i^{1}x^{i} & \cdots & i^{x}x^{i} & sum \end{bmatrix} \times \texttt{转移矩阵} =\begin{bmatrix} (i + 1)^{0}x^{i + 1} & (i + 1)^{1}x^{i + 1} & \cdots & (i + 1)^{x}x^{i + 1} & sum \end{bmatrix} \]

(因为转移矩阵太大了就给放下面来了)

\[\begin{bmatrix} 1k & 1k & 1k & 1k & \cdots & \binom{x}{0}k & 0\\ 0 & 1k & 2k & 3k & \cdots & \binom{x}{1}k & 0\\ 0 & 0 & 1k & 3k & \cdots & \binom{x}{2}k & 0\\ 0 & 0 & 0 & 1k & \cdots & \binom{x}{3}k & 0\\ \vdots & \vdots & \vdots& \vdots& \ddots & \vdots\\ 0 & 0 & 0 & 0 & \cdots & \binom{x}{x}k & 1\\ 0 & 0 & 0 & 0 & \cdots & 0 & 1\\ \end{bmatrix} \]

最后一列是给 \(sum\) 转移的。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, x, siz = 52, mod = 998244353;
struct modint {
	int x;
	modint(ll v = 0): x((v % mod + mod) % mod) {}
	friend modint operator + (const modint& x, const modint& y) {return (ll)x.x + y.x >= mod ? (ll)x.x + y.x - mod : x.x + y.x;}
	friend modint operator * (const modint& x, const modint& y) {return (ll)x.x * y.x % mod;}
	modint operator = (const modint& _) {x = _.x; return *this;}
	modint operator += (const modint& _) {*this = *this + _; return *this;}
	friend ostream& operator << (ostream& out, const modint& _) {return out << _.x;}
} res;
struct matrix {
	modint val[52][52];
	matrix(modint v = 0) {
		for(int i = 0; i < siz; ++i) {
			for(int j = 0; j < siz; ++j) {
				val[i][j] = (i == j ? v : 0);
			}
		}
	}
	void operator *= (const matrix& _) {
		static matrix res;
		for(int i = 0; i < siz; ++i) {
			for(int j = 0; j < siz; ++j) {
				res.val[i][j] = 0;
				for(int k = 0; k < siz; ++k) {
					res.val[i][j] += val[i][k] * _.val[k][j];
				}
			}
		}
		*this = res;
	}
} a, ans;
matrix ksm(matrix& x, ll y) {
	matrix ret(1);
	while(y) {
		if(y & 1) ret *= x;
		x *= x, y >>= 1;
	}
	return ret;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	while(cin >> n >> x >> mod) {
		if(!(~n) && !(~x) && !(~mod)) break;
		siz = x + 2;
		for(int i = 0; i <= x; ++i) {
			a.val[0][i] = x;
			for(int j = 1; j <= i; ++j) {
				a.val[j][i] = a.val[j - 1][i - 1] + a.val[j][i - 1];
			}
			for(int j = i + 1; j < siz; ++j) a.val[j][i] = 0;
		}
		for(int i = 0; i < siz; ++i) a.val[i][x + 1] = 0;
		a.val[x][x + 1] = a.val[x + 1][x + 1] = 1;
		ans = ksm(a, n);
		res = 0;
		for(int i = 0; i <= x; ++i) res += x * ans.val[i][x + 1];
		cout << res << '\n';
	}
	return 0;
}

C. Height All the Same

目标为「推平」,那肯定就和高度无关。

并且每次都放两个方块,那么只考虑每个位置的奇偶性即可(或者说模 \(2\) 意义下的高度,下面直接以模 \(2\) 意义下的高度来讨论)。

操作二不会改变高度,故忽略。

然后分两个小情况:

  1. \(n \times m\) 为奇数,那么什么初始局面都可以。

此时要么有偶数个 \(0\),要么有偶数个 \(1\),那么对着偶数的那一部分进行操作一即可,如果没有相邻的,那么随便找一对相邻的 \(1\)\(0\) 进行操作一,就可以让它们「交换」。

  1. \(n \times m\) 为偶数,\(0\)\(1\) 的数量同时为偶数即可。

必要性不用证明了,充分性考虑反证法。

如果 \(0\)\(1\) 的数量同时为奇数,操作可以使得 \(0/1\) 的数量 \(\pm 2\),但因为它们的数量都是奇数,操作后仍为奇数,始终无法推平,所以奇数的情况不可行。

那么此时答案就是:

\[\sum\limits_{i = 0}^{\frac{nm}{2}}\binom{nm}{2i}oddcnt^{2i}evencnt^{nm - 2i} \]

没法使用二项式定理,难受,直接补全!

\[\sum\limits_{i = 0}^{\frac{nm}{2}}\binom{nm}{2i}oddcnt^{2i}evencnt^{nm - 2i} + \sum\limits_{i = 1}^{\frac{nm}{2}}\binom{nm}{2i - 1}oddcnt^{2i - 1}evencnt^{nm - 2i + 1} \]

它等于 \((oddcnt + evencnt)^{nm}\)

怎么把多出来的项消掉?发现 \((-1)^{2i} = 1, (-1)^{2i - 1} = -1\),尝试丢进去:

\[\sum\limits_{i = 0}^{\frac{nm}{2}}\binom{nm}{2i}(-oddcnt)^{2i}evencnt^{nm - 2i} + \sum\limits_{i = 1}^{\frac{nm}{2}}\binom{nm}{2i - 1}(-oddcnt)^{2i - 1}evencnt^{nm - 2i + 1} \]

它等于 \((evencnt - oddcnt)^{nm} = (oddcnt - evencnt)^{nm}\)\(nm\) 为偶数)。

而答案就是 \(\dfrac{(oddcnt + evencnt)^{nm} + (oddcnt - evencnt)^{nm}}{2}\)

随便怎么算都行啦。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int mod = 998244353;
struct modint {
	int x;
	modint(ll v = 0): x((v % mod + mod) % mod) {}
	friend modint ksm(modint x, ll y) {
		modint ret = 1;
		while(y) {if(y & 1) ret *= x; x *= x, y >>= 1;}
		return ret;
	}
	friend modint operator + (const modint& x, const modint& y) {return x.x + y.x >= mod ? x.x + y.x - mod : x.x + y.x;}
	friend modint operator - (const modint& x, const modint& y) {return x.x < y.x ? x.x - y.x + mod : x.x - y.x;}
	friend modint operator * (const modint& x, const modint& y) {return (ll)x.x * y.x % mod;}
	friend modint operator / (const modint& x, const modint& y) {return x * ksm(y, mod - 2);}
	modint operator = (const modint& _) {x = _.x; return *this;}
	modint operator += (const modint& _) {*this = *this + _; return *this;}
	modint operator -= (const modint& _) {*this = *this - _; return *this;}
	modint operator *= (const modint& _) {*this = *this * _; return *this;}
	modint operator /= (const modint& _) {*this = *this / _; return *this;}
	bool operator == (const modint& _) const {return x == _.x;}
	bool operator != (const modint& _) const {return x != _.x;}
	friend istream& operator >> (istream& in, modint& _) {static ll tmp; return in >> tmp; _.x = tmp % mod;}
	friend ostream& operator << (ostream& out, const modint& _) {return out << _.x;}
};
ll n, m, l, r;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> l >> r;
	if((n * m) & 1) {
		cout << ksm(modint(r - l + 1), n * m);
		return 0;
	}
	cout << (ksm(modint(r - l + 1), n * m) + ksm(modint((r & 1) - ((l - 1) & 1)), n * m)) / 2;
	return 0;
}

D. Binomial Coefficient is Fun

智力讲过了,不写了,贴一个洛谷题解区敷衍一下表示一下。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int mod = 1000000007;
struct modint {
	int x;
	modint(ll v = 0): x((v % mod + mod) % mod) {}
	friend modint ksm(modint x, ll y) {
		modint ret = 1;
		while(y) {if(y & 1) ret *= x; x *= x, y >>= 1;}
		return ret;
	}
	friend modint operator + (const modint& x, const modint& y) {return x.x + y.x >= mod ? x.x + y.x - mod : x.x + y.x;}
	friend modint operator - (const modint& x, const modint& y) {return x.x < y.x ? x.x - y.x + mod : x.x - y.x;}
	friend modint operator * (const modint& x, const modint& y) {return (ll)x.x * y.x % mod;}
	friend modint operator / (const modint& x, const modint& y) {return x * ksm(y, mod - 2);}
	modint operator = (const modint& _) {x = _.x; return *this;}
	modint operator += (const modint& _) {*this = *this + _; return *this;}
	modint operator -= (const modint& _) {*this = *this - _; return *this;}
	modint operator *= (const modint& _) {*this = *this * _; return *this;}
	modint operator /= (const modint& _) {*this = *this / _; return *this;}
	bool operator == (const modint& _) const {return x == _.x;}
	bool operator != (const modint& _) const {return x != _.x;}
	friend istream& operator >> (istream& in, modint& _) {static ll tmp; in >> tmp; _.x = tmp % mod; return in;}
	friend ostream& operator << (ostream& out, const modint& _) {return out << _.x;}
};
int n, a, sum;
modint m, num = 1, den = 1;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; ++i) {
		cin >> a;
		sum += a;
	}
	sum += n;
	m += n;
	for(int i = 1; i <= sum; ++i) {
		num *= m - i + 1;
		den *= i;
	}
	cout << num / den;
	return 0;
}

E. Anton and School - 2

什么什么蒙德卷积的板板。

钦定选择的最后一个 \(\texttt{(}\),令 \(pre_{i}\) 表示 \(1 \sim i\) 中有多少 \(\texttt{(}\)\(suf_{i}\) 表示 \(i \sim n\) 中有多少 \(\texttt{)}\)\(n\) 为字符串长度)。

答案就是:

\[\sum\limits_{1 \leqslant i \leqslant n \land s_{i} = \texttt{(}}\sum\limits_{j = 1}^{\min{pre_{i}, suf_{i}}}\binom{pre_{i} - 1}{j - 1}\binom{suf_{i}}{j} \]

后面那一部分直接什么什么蒙德卷积就好了。

变成:

\[\sum\limits_{1 \leqslant i \leqslant n \land s_{i} = \texttt{(}}\binom{pre_{i} +suf_{i} - 1}{pre_{i}} \]

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int mod = 1000000007;
int n, pre[200005], suf[200005];
string s;
struct modint {
	int x;
	modint(ll v = 0): x((v % mod + mod) % mod) {}
	friend modint ksm(modint x, ll y) {
		modint ret = 1;
		while(y) {if(y & 1) ret *= x; x *= x, y >>= 1;}
		return ret;
	}
	friend modint operator + (const modint& x, const modint& y) {return x.x + y.x >= mod ? x.x + y.x - mod : x.x + y.x;}
	friend modint operator - (const modint& x, const modint& y) {return x.x < y.x ? x.x - y.x + mod : x.x - y.x;}
	friend modint operator * (const modint& x, const modint& y) {return (ll)x.x * y.x % mod;}
	friend modint operator / (const modint& x, const modint& y) {return x * ksm(y, mod - 2);}
	modint operator = (const modint& _) {x = _.x; return *this;}
	modint operator += (const modint& _) {*this = *this + _; return *this;}
	modint operator -= (const modint& _) {*this = *this - _; return *this;}
	modint operator *= (const modint& _) {*this = *this * _; return *this;}
	modint operator /= (const modint& _) {*this = *this / _; return *this;}
	bool operator == (const modint& _) const {return x == _.x;}
	bool operator != (const modint& _) const {return x != _.x;}
	friend istream& operator >> (istream& in, modint& _) {static ll tmp; return in >> tmp; _.x = tmp % mod;}
	friend ostream& operator << (ostream& out, const modint& _) {return out << _.x;}
};
modint ans, fct[200005], inv[200005];
modint c(int n, int m) {
	if(m > n) return 0;
	return fct[n] * inv[m] * inv[n - m];
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	fct[0] = 1;
	for(int i = 1; i <= 200000; ++i) fct[i] = fct[i - 1] * i;
	inv[200000] = 1 / fct[200000];
	for(int i = 200000; i >= 1; --i) inv[i - 1] = inv[i] * i;
	cin >> s;
	n = s.length();
	s = "V" + s;
	for(int i = 1; i <= n; ++i) pre[i] = pre[i - 1] + (s[i] == '(');
	for(int i = n; i >= 1; --i) suf[i] = suf[i + 1] + (s[i] == ')');
	for(int i = 1; i <= n; ++i) {
		if(s[i] == '(') {
			ans += c(pre[i] + suf[i] - 1, pre[i]);
		}
	}
	cout << ans;
	return 0;
}

F. Card

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, k;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> k;
	cout << fixed << setprecision(3) << (n + 1.0) * k / (m + 1.0);
	return 0;
}

G. Team Work

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int mod = 1000000007;
struct modint {
	int x;
	modint(ll v = 0): x((v % mod + mod) % mod) {}
	friend modint ksm(modint x, ll y) {
		modint ret = 1;
		while(y) {if(y & 1) ret *= x; x *= x, y >>= 1;}
		return ret;
	}
	friend modint operator + (const modint& x, const modint& y) {return x.x + y.x >= mod ? x.x + y.x - mod : x.x + y.x;}
	friend modint operator - (const modint& x, const modint& y) {return x.x < y.x ? x.x - y.x + mod : x.x - y.x;}
	friend modint operator * (const modint& x, const modint& y) {return (ll)x.x * y.x % mod;}
	friend modint operator / (const modint& x, const modint& y) {return x * ksm(y, mod - 2);}
	modint operator = (const modint& _) {x = _.x; return *this;}
	modint operator += (const modint& _) {*this = *this + _; return *this;}
	modint operator -= (const modint& _) {*this = *this - _; return *this;}
	modint operator *= (const modint& _) {*this = *this * _; return *this;}
	modint operator /= (const modint& _) {*this = *this / _; return *this;}
	bool operator == (const modint& _) const {return x == _.x;}
	bool operator != (const modint& _) const {return x != _.x;}
	friend istream& operator >> (istream& in, modint& _) {static ll tmp; return in >> tmp; _.x = tmp % mod;}
	friend ostream& operator << (ostream& out, const modint& _) {return out << _.x;}
};
int n, k;
bool vis[5005][5005];
modint power[5005], dp[5005][5005];
modint dfs(int sub, int m) {
	if(!m) return power[sub];
	if(vis[sub][m]) return dp[sub][m];
	modint& ret = dp[sub][m];
	ret = (n - sub) * dfs(sub + 1, m - 1) + sub * dfs(sub, m - 1);
	vis[sub][m] = true;
	return ret;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> k;
	power[0] = ksm(modint(2), n);
	for(int i = 1; i <= min(n, 5000); ++i) power[i] = power[i - 1] / 2;
	cout << dfs(0, k);
	return 0;
}
posted @ 2024-03-06 21:03  A_box_of_yogurt  阅读(14)  评论(1编辑  收藏  举报
Document