Solution Set【2024.1.21】
[MtOI2018] 情侣?给我烧了!
设 \(f_{k, n}\) 表示在有 \(n\) 对情侣的情况下钦定 \(k\) 对情侣是和睦的方案数,\(g_{k, n}\) 表示在有 \(n\) 对情侣的情况下恰好有 \(k\) 对情侣是和睦的方案数。考察二者之间的关系,我们有:
根据二项式反演,我们有:
考虑如何求出 \(f_{k, n}\),我们有:
上述式子的含义是:首先从 \(n\) 对情侣中选出 \(k\) 对情侣,然后将这 \(k\) 对情侣钦定为和睦的,然后将这 \(k\) 对情侣的位置确定下来,最后将剩下的 \(2n - 2k\) 个人的位置确定下来。
代入上述关系式,我们有:
取 \(i = m - k\),我们有:
记 \(h_{m} = \sum\limits_{i = 0}^{m} \left(-1\right)^{i} \frac{2^{i}}{i!} \times \left(\frac{1}{\left(m - i\right)!}\right)^2 \times \left(2m - 2i\right)!\),我们有:
设 \(N = \max n\),我们可以在 \(\mathcal{O}(N^2)\) 的时间内预处理出 \(h_{m}\) 的值,然后在 \(\mathcal{O}(1)\) 的时间内求出 \(g_{k, n}\) 的值。
复杂度为 \(\mathcal{O}(N^2 + \sum n)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
namespace MODINT_WITH_FIXED_MOD {
constexpr valueType MOD = 998244353;
template<typename T1, typename T2>
void Inc(T1 &a, T2 b) {
a = a + b;
if (a >= MOD)
a -= MOD;
}
template<typename T1, typename T2>
void Dec(T1 &a, T2 b) {
a = a - b;
if (a < 0)
a += MOD;
}
template<typename T1, typename T2>
T1 sum(T1 a, T2 b) {
return a + b >= MOD ? a + b - MOD : a + b;
}
template<typename T1, typename T2>
T1 sub(T1 a, T2 b) {
return a - b < 0 ? a - b + MOD : a - b;
}
template<typename T1, typename T2>
T1 mul(T1 a, T2 b) {
return (long long) a * b % MOD;
}
template<typename T1, typename T2>
void Mul(T1 &a, T2 b) {
a = (long long) a * b % MOD;
}
template<typename T1, typename T2>
T1 pow(T1 a, T2 b) {
T1 result = 1;
while (b > 0) {
if (b & 1)
Mul(result, a);
Mul(a, a);
b = b >> 1;
}
return result;
}
} // namespace MODINT_WITH_FIXED_MOD
using namespace MODINT_WITH_FIXED_MOD;
class BinomialCoefficient {
private:
valueType N;
ValueVector _Fact, _InvFact;
public:
BinomialCoefficient() = default;
BinomialCoefficient(valueType n) : N(n), _Fact(N + 1, 1), _InvFact(N + 1, 1) {
for (valueType i = 1; i <= N; ++i)
_Fact[i] = mul(_Fact[i - 1], i);
_InvFact[N] = pow(_Fact[N], MOD - 2);
for (valueType i = N - 1; i >= 0; --i)
_InvFact[i] = mul(_InvFact[i + 1], i + 1);
}
valueType operator()(valueType n, valueType m) {
if (n < 0 || m < 0 || n < m)
return 0;
if (m > N)
throw std::out_of_range("BinomialCoefficient::operator() : m > N");
if (n <= N)
return mul(_Fact[n], mul(_InvFact[m], _InvFact[n - m]));
valueType result = 1;
for (valueType i = 0; i < m; ++i)
Mul(result, n - i);
Mul(result, _InvFact[m]);
return result;
}
valueType Fact(valueType n) {
if (n < 0)
return 0;
if (n > N)
throw std::out_of_range("BinomialCoefficient::Fact : n > N");
return _Fact[n];
}
valueType InvFact(valueType n) {
if (n < 0)
return 0;
if (n > N)
throw std::out_of_range("BinomialCoefficient::InvFact : n > N");
return _InvFact[n];
}
};
constexpr valueType V = 1000;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType T;
std::cin >> T;
BinomialCoefficient C(2 * V);
ValueVector H(V + 1);
ValueVector Pow2(V + 1, 0);
Pow2[0] = 1;
for (valueType i = 1; i <= V; ++i)
Pow2[i] = mul(Pow2[i - 1], 2);
for (valueType i = 0; i <= V; ++i) {
for (valueType j = 0; j <= i; ++j) {
if (j & 1)
Dec(H[i], mul(mul(C.InvFact(j), Pow2[j]), mul(mul(C.InvFact(i - j), C.InvFact(i - j)), C.Fact(2 * i - 2 * j))));
else
Inc(H[i], mul(mul(C.InvFact(j), Pow2[j]), mul(mul(C.InvFact(i - j), C.InvFact(i - j)), C.Fact(2 * i - 2 * j))));
}
}
for (valueType testcase = 0; testcase < T; ++testcase) {
valueType N;
std::cin >> N;
ValueVector G(N + 1, 0);
for (valueType i = 0; i <= N; ++i)
G[i] = mul(mul(C.Fact(N), C.Fact(N)), mul(mul(C.InvFact(i), Pow2[i]), H[N - i]));
for (valueType i = 0; i <= N; ++i)
std::cout << G[i] << '\n';
}
std::cout << std::flush;
return 0;
}
[HAOI2015] 按位或
考虑 Min-Max 容斥,我们有:
设全集为 \(U\),我们要求的便是 \(E\left(\max\limits_{x \in U} x\right)\)。那么我们考虑对于每个集合 \(S\) 求出 \(E\left(\min\limits_{x \in S} x\right)\)。
考虑 \(E\left(\min\limits_{x \in S} x\right)\) 的意义,即 \(S\) 中至少有一个元素被选中的期望步数,考虑其中至少一个元素被选中的概率,我们有:
其中 \(P(T)\) 表示 \(T\) 所代表的二进制数出现的概率。
考虑如何计算 \(\sum\limits_{T \cap S \neq \varnothing} P(T)\),正难则反,我们考虑计算 \(\sum\limits_{T \cap S = \varnothing} P(T)\),即 \(S\) 中的元素都没有被选中的概率。发现我们有:
因此对 \(P\) 序列做高维前缀和即可。复杂度为 \(\mathcal{O}(n2^n)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef long double realType;
typedef std::vector<realType> RealVector;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
valueType const M = 1 << N;
RealVector P(M);
for (valueType i = 0; i < M; ++i)
std::cin >> P[i];
for (valueType i = 0; i < N; ++i) {
for (valueType j = 0; j < M; ++j) {
if ((j >> i) & 1)
P[j] += P[j ^ (1 << i)];
}
}
realType ans = 0;
for (valueType s = 1; s < M; ++s) {
if (__builtin_popcount(s) & 1) {
ans += static_cast<realType>(1) / static_cast<realType>(1 - P[(M - 1) ^ s]);
} else {
ans -= static_cast<realType>(1) / static_cast<realType>(1 - P[(M - 1) ^ s]);
}
}
if (std::isinf(ans) || std::isnan(ans))
std::cout << "INF" << std::endl;
else
std::cout << std::fixed << std::setprecision(10) << ans << std::endl;
return 0;
}