Solution Set【2024.1.17】
[ABC298Ex] Sum of Min of Length
在下文的推导中假设
首先我们可以发现,我们可以找到
考虑如何计算答案,首先以求
其中
直接计算即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
valueType N, Q;
ValueMatrix G;
ValueVector InTree, OutTree, size, depth, father, son, top;
ValueVector leftBound, rightBound, node;
void dfs(valueType x, valueType from) {
father[x] = from;
size[x] = 1;
depth[x] = depth[from] + 1;
InTree[x] = 0;
for (auto const &to : G[x]) {
if (to == from)
continue;
dfs(to, x);
size[x] += size[to];
InTree[x] += InTree[to] + size[to];
if (son[x] == 0 || size[to] > size[son[x]])
son[x] = to;
}
}
void build(valueType x, valueType from, valueType _top) {
static valueType dfsCount = 0;
top[x] = _top;
if (from != 0)
OutTree[x] = OutTree[from] - (InTree[x] + size[x]) + (N - size[x]) + InTree[x];
leftBound[x] = ++dfsCount;
node[dfsCount] = x;
if (son[x] != 0)
build(son[x], x, _top);
for (auto const &to : G[x]) {
if (to == from || to == son[x])
continue;
build(to, x, to);
}
rightBound[x] = dfsCount;
}
valueType GetLCA(valueType u, valueType v) {
while (top[u] != top[v]) {
if (depth[top[u]] < depth[top[v]])
std::swap(u, v);
u = father[top[u]];
}
return depth[u] < depth[v] ? u : v;
}
valueType GetDist(valueType u, valueType v) {
if (u < 1 || u > N || v < 1 || v > N)
return 0;根据二项式反演,我们有:
因此我们可以在
的时间内求出 ,完成第二部分的计数。
valueType Jump(valueType u, valueType k) {
while (u != 0) {
if (depth[u] - depth[top[u]] + 1 <= k) {
k -= depth[u] - depth[top[u]] + 1;
u = father[top[u]];
} else {
return node[leftBound[u] - k];
}
}
return 0;
}
valueType solve(valueType u, valueType v) {
if (u == v)
return OutTree[u];
if (depth[u] > depth[v])
std::swap(u, v);
valueType lca = GetLCA(u, v);
valueType const dist = GetDist(u, v);
valueType const mid = Jump(v, dist / 2);
valueType ans = 0;
ans += OutTree[v];
ans -= (OutTree[mid] - InTree[mid]) + (N - size[mid]) * GetDist(mid, v);
ans += OutTree[u];
ans -= InTree[mid] + size[mid] * GetDist(mid, u);
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::cin >> N;
G.resize(N + 1);
InTree.resize(N + 1);
OutTree.resize(N + 1);
size.resize(N + 1);
depth.resize(N + 1);
father.resize(N + 1);
son.resize(N + 1);
top.resize(N + 1);
leftBound.resize(N + 1);
rightBound.resize(N + 1);
node.resize(N + 1);
for (valueType i = 1; i < N; ++i) {
valueType u, v;
std::cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
OutTree[1] = InTree[1];
build(1, 0, 1);
std::cin >> Q;
for (valueType q = 0; q < Q; ++q) {
valueType u, v;
std::cin >> u >> v;
std::cout << solve(u, v) << '\n';
}
std::cout << std::flush;
return 0;
}
信封问题
定义在排列
根据二项式反演,我们有:
考虑如何求
而我们要求的是错排数,即
直接计算即可,复杂度
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
ValueVector Fact(N + 1, 1);
for (valueType i = 1; i <= N; ++i)
Fact[i] = Fact[i - 1] * i;
valueType ans = 0;
for (valueType i = 0; i <= N; ++i) {
if (i & 1)
ans -= Fact[N] / Fact[i];
else
ans += Fact[N] / Fact[i];
}
std::cout << ans << std::endl;
return 0;
}
已经没有什么好害怕的了
首先我们可以将题目要求的转化为恰好存在
首先将序列
其中
发现上述 DP 求出的方案数与题意不符,考虑如何修正。由于上述 DP 求出的是至少存在
根据二项式反演,我们有:
考虑如何求出
直接计算即可,复杂度
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 = 1e9 + 9;
template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
a = a + b;
if (a >= mod)
a -= mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
a = a - b;
if (a < 0)
a += mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
return a + b >= mod ? a + b - mod : a + b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
return a - b < 0 ? a - b + mod : a - b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
return (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
a = (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
T1 result = 1;
while (b > 0) {
if (b & 1)
Mul(result, a, mod);
Mul(a, a, mod);
b = b >> 1;
}
return result;
}
}// namespace MODINT_WITH_FIXED_MOD
using namespace MODINT_WITH_FIXED_MOD;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, K;
std::cin >> N >> K;
if ((N - K) & 1) {
std::cout << 0 << std::endl;
return 0;
}
ValueVector 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);
auto C = [&](valueType n, valueType m) -> valueType {
if (n < 0 || m < 0 || n < m)
return 0;
return mul(Fact[n], mul(InvFact[m], InvFact[n - m]));
};
ValueMatrix F(N + 1, ValueVector(N + 1, 0));
F[0][0] = 1;
ValueVector A(N + 1, 0), B(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
for (valueType i = 1; i <= N; ++i)
std::cin >> B[i];
std::sort(A.begin() + 1, A.end());
std::sort(B.begin() + 1, B.end());
std::swap(A, B);
valueType pointer = 0;
F[0][0] = 1;
for (valueType i = 1; i <= N; ++i) {
while (pointer < N && A[pointer + 1] < B[i])
++pointer;
for (valueType j = 0; j <= N; ++j) {
F[i][j] = F[i - 1][j];
if (j == 0)
continue;
Inc(F[i][j], mul(pointer - j + 1, F[i - 1][j - 1]));
}
}
K = K + (N - K) / 2;
valueType ans = 0;
for (valueType i = K; i <= N; ++i) {
if ((i - K) & 1) {
Dec(ans, mul(C(i, K), mul(F[N][i], Fact[N - i])));
} else {
Inc(ans, mul(C(i, K), mul(F[N][i], Fact[N - i])));
}
}
std::cout << ans << std::endl;
return 0;
}
[JLOI2016] 成绩比较
首先,我们将方案数分为两部分:
- 选出
名被碾压的同学并分配每位同学每个学科的分数与 B 神的关系。 - 对每个学科的分数进行排列。
对第一部分做出一点解释:将计算碾压同学的方案数和分配分数与 B 神的关系放到一起是因为一位同学各学科的分数与 B 神的关系决定了其是否被碾压,因此需要将其放到一起考虑。
下面考虑如何计数,首先是第一部分。若不考虑除我们钦定外的同学是否被碾压,那么我们钦定已经有
发现这样计数在这
根据二项式反演,我们有:
因此我们可以在
下面考虑第二部分,如何统计每个学科的分数排列方案数。首先由于各学科是独立的,因此我们只需要考虑如何求解单独一门学科的方案数即可。设
由于分数的值域较大,不能直接计算,因此我们考虑枚举有多少种分数,设有
但是我们可以发现上述计数方法存在一个问题,即可能会计算到实际分数种类不足
根据二项式反演,我们有:
因此我们可以在
总复杂度为
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::map<valueType, valueType> ValueMap;
typedef std::vector<ValueMap> MapVector;
namespace MODINT_WITH_FIXED_MOD {
constexpr valueType MOD = 1e9 + 7;
template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
a = a + b;
if (a >= mod)
a -= mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
a = a - b;
if (a < 0)
a += mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
return a + b >= mod ? a + b - mod : a + b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
return a - b < 0 ? a - b + mod : a - b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
return (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
a = (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
if (a == 0)
return b == 0 ? 1 : 0;
T1 result = 1;
while (b > 0) {
if (b & 1)
Mul(result, a, mod);
Mul(a, a, mod);
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;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M, K;
std::cin >> N >> M >> K;
BinomialCoefficient C(N);
ValueVector U(M + 1, 0), R(M + 1, 0);
for (valueType i = 1; i <= M; ++i)
std::cin >> U[i];
for (valueType i = 1; i <= M; ++i)
std::cin >> R[i];
valueType ans = 1;
{// part one
ValueVector F(N + 1, 0);
for (valueType i = 0; i < N; ++i) {
valueType const remain = N - i - 1;
F[i] = C(N - 1, i);
for (valueType j = 1; j <= M; ++j)
Mul(F[i], C(remain, R[j] - 1));
}
valueType sum = 0;
for (valueType i = K; i <= N; ++i) {
if ((i - K) & 1) {
Dec(sum, mul(F[i], C(i, K)));
} else {
Inc(sum, mul(F[i], C(i, K)));
}
}
Mul(ans, sum);
}
std::cerr << "ans = " << ans << std::endl;
{// part two
for (valueType m = 1; m <= M; ++m) {
ValueVector F(N + 1, 0);
for (valueType t = 1; t <= N; ++t) {
for (valueType i = 1; i <= t; ++i)
Inc(F[t], mul(pow(i, N - R[m]), pow(t - i, R[m] - 1)));
}
valueType sum = 0;
for (valueType i = 1; i <= N; ++i) {
valueType S = 0;
for (valueType j = 1; j <= i; ++j) {
if ((i - j) & 1) {
Dec(S, mul(F[j], C(i, j)));
} else {
Inc(S, mul(F[j], C(i, j)));
}
}
Inc(sum, mul(S, C(U[m], i)));
}
Mul(ans, sum);
}
}
std::cout << ans << std::endl;
return 0;
}
CF1228E Another Filling the Grid
设
根据二项式反演,我们有:
考虑如何求解
其中
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 = 1e9 + 7;
template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
a = a + b;
if (a >= mod)
a -= mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
a = a - b;
if (a < 0)
a += mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
return a + b >= mod ? a + b - mod : a + b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
return a - b < 0 ? a - b + mod : a - b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
return (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
a = (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
T1 result = 1;
while (b > 0) {
if (b & 1)
Mul(result, a, mod);
Mul(a, a, mod);
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;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, K;
std::cin >> N >> K;
if (K == 1) {
std::cout << 1 << std::endl;
return 0;
}
BinomialCoefficient C(N);
ValueMatrix F(N + 1, ValueVector(N + 1, 0));
for (valueType i = 0; i <= N; ++i) {
for (valueType j = 0; j <= N; ++j) {
valueType const count = i * N + j * N - i * j;
F[i][j] = 1;
Mul(F[i][j], C(N, i));
Mul(F[i][j], C(N, j));
Mul(F[i][j], pow(K - 1, count));
Mul(F[i][j], pow(K, N * N - count));
}
}
valueType ans = 0;
for (valueType i = 0; i <= N; ++i) {
for (valueType j = 0; j <= N; ++j) {
if ((i + j) & 1) {
Dec(ans, F[i][j]);
} else {
Inc(ans, F[i][j]);
}
}
}
std::cout << ans << std::endl;
return 0;
}
CF997C Sky Full of Stars
考虑求出没有任何一行或一列颜色相同的方案数,使用总方案数减去其就是答案。
设
根据二项式反演,我们有:
而我们要求的是
考虑如何计算
- 若
,那么我们有 。 - 若
或 ,那么我们有 ,这是因为被选择的 行 / 列颜色各自独立,剩余的格子颜色可以任意填。 - 若
,那么我们有 。
依次考虑其对答案的贡献,对于前两种情况可以直接分别在
发现我们要求的实际上为:
其可以在
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;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
BinomialCoefficient C(N);
valueType ans = 0;
Inc(ans, pow(3, N * N));
for (valueType i = 1; i <= N; ++i) {
if (i & 1) {
Dec(ans, mul(mul(2, C(N, i)), pow(3, N * (N - i) + i)));
} else {
Inc(ans, mul(mul(2, C(N, i)), pow(3, N * (N - i) + i)));
}
}
for (valueType i = 1; i <= N; ++i) {
if (i & 1) {
Dec(ans, mul(mul(3, C(N, i)), sub(pow(sub(pow(3, N - i), 1), N), pow(3, N * (N - i)))));
} else {
Inc(ans, mul(mul(3, C(N, i)), sub(pow(sub(pow(3, N - i), 1), N), pow(3, N * (N - i)))));
}
}
ans = sub(pow(3, N * N), ans);
std::cout << ans << std::endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现