Solution Set【2024.1.7】
CF1919C Grouping Increases
考虑维护出当前的两个子序列,然后贪心的选择将当前元素放入哪个子序列中。
发现决策只与两个子序列的末尾元素和当前元素有关,不妨设两个子序列末尾元素分别为 \(x, y\),并且有 \(x \le y\)。
-
若 \(a_i \le x\),那么无论将 \(a_i\) 放入哪个子序列,均不会产生代价,为了使得后续可以插入的元素范围更大,选择将 \(a_i\) 放入 \(x\) 所在的子序列中。
-
若 \(y < a_i\),那么无论将 \(a_i\) 放入哪个子序列,均会产生代价,为了使得后续可以插入的元素范围更大,选择将 \(a_i\) 放入 \(y\) 所在的子序列中。
-
若 \(x \le a_i < y\),那么若将 \(a_i\) 插入 \(x\) 所在的子序列中,会产生代价,而将 \(a_i\) 插入 \(y\) 所在的子序列中不会产生代价。不妨假设我们在插入剩余 \(a_{i + 1}, \cdots, a_n\) 的过程中均采取同一种决策,那么可以发现若插入 \(x\) 所在的子序列中的方案优于插入 \(y\) 所在的子序列中的方案,那么在插入该元素后,两个子序列的末尾元素相同。又因为在这之前插入 \(x\) 所在的子序列中的方案多一点代价,因此无论如何插入 \(y\) 所在的子序列中不劣于插入 \(x\) 所在的子序列中的方案。因此,我们可以在插入 \(a_i\) 时,选择将 \(a_i\) 插入 \(y\) 所在的子序列中。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<ValueMatrix> ValueCube;
typedef std::string string;
typedef std::vector<string> StringVector;
typedef std::vector<bool> bitset;
typedef std::vector<bitset> BitMatrix;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::vector<TupleVector> TupleMatrix;
typedef std::set<valueType> ValueSet;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType T;
std::cin >> T;
for (valueType testcase = 0; testcase < T; ++testcase) {
valueType N;
std::cin >> N;
ValueVector A(N + 1);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
if (N <= 2) {
std::cout << 0 << '\n';
continue;
}
valueType ans = 0;
ValueVector L, R;
L.push_back(N);
R.push_back(N);
L.reserve(N);
R.reserve(N);
for (valueType i = 1; i <= N; ++i) {
bool left = A[i] > L.back(), right = A[i] > R.back();
if (left == right) {
if (L.back() < R.back()) {
ans += L.back() < A[i];
L.push_back(A[i]);
} else {
ans += R.back() < A[i];
R.push_back(A[i]);
}
} else {
if (!left) {
ans += L.back() < A[i];
L.push_back(A[i]);
} else {
ans += R.back() < A[i];
R.push_back(A[i]);
}
}
}
std::cout << ans << '\n';
}
std::cout << std::flush;
return 0;
}
CF1919D 01 Tree
考虑深度最大的叶子节点,不难发现其兄弟节点一定是一个深度比其小 \(1\) 的叶子节点,这就意味着他们的 dfs 序是相邻的。
因此我们可以每次选取深度最大的叶子节点,检查其 dfs 序两侧是否存在深度比其小 \(1\) 的叶子节点,若存在则将这两个叶子节点删去,会产生一个新的深度比最大深度小 \(1\) 的叶子节点即其原来的父亲节点,将其插入到 dfs 序列中继续执行删除操作即可。若其 dfs 序两侧不存在深度比其小 \(1\) 的叶子节点,那么我们可以将其删去,那么不存在合法的方案。
具体实现时,可以使用一个堆来维护当前深度最大的叶子节点,使用链表来维护 dfs 序列。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<ValueMatrix> ValueCube;
typedef std::string string;
typedef std::vector<string> StringVector;
typedef std::vector<bool> bitset;
typedef std::vector<bitset> BitMatrix;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::vector<TupleVector> TupleMatrix;
typedef std::set<valueType> ValueSet;
typedef std::stack<valueType> ValueStack;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType T;
std::cin >> T;
for (valueType testcase = 0; testcase < T; ++testcase) {
valueType N;
std::cin >> N;
ValueVector A(N + 1);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
valueType ZeroCount = 0;
for (valueType i = 1; i <= N; ++i)
if (A[i] == 0)
++ZeroCount;
if (ZeroCount != 1) {
std::cout << "No\n";
continue;
}
bool ans = true;
bitset visited(N + 1, false);
// ValueVector stack;
valueType node = 0;
for (valueType i = 1; i <= N && ans; ++i) {
valueType x = A[i];
// if (stack.empty() || stack.back() < x) {
// stack.push_back(x);
// } else if (stack.back() > x) {
// for (valueType j = stack.back(); j > x; --j) {
// if (stack.empty() || stack.back() != j) {
// ans = false;
//
// break;
// } else {
// stack.pop_back();
// }
// }
//
// stack.push_back(x);
// }
if (x <= node) {
while (node > x) {
if (!visited[node]) {
ans = false;
break;
}
--node;
}
} else {
while (node < x) {
visited[++node] = false;
}
}
visited[x] = true;
}
while (node >= 0 && ans) {
if (!visited[node]) {
ans = false;
break;
}
--node;
}
// if (stack.empty() || !ans) {
// std::cout << "No\n";
//
// continue;
// }
//
// for (valueType i = stack.back(); i >= 0 && ans; --i) {
// if (stack.empty() || stack.back() != i) {
// ans = false;
//
// break;
// } else {
// stack.pop_back();
// }
// }
if (!ans) {
std::cout << "No\n";
continue;
} else {
std::cout << "Yes\n";
}
}
std::cout << std::flush;
return 0;
}
CF1919E Counting Prefixes
考虑枚举序列的和。不妨设序列的和为 \(s\),那么为了满足前缀和最大值的要求,我们可以初始构造出一个形如 \(1, 1, 1, 1, \cdots, 1, 1, 1, -1, -1, \cdots, -1, -1\) 的序列,其中其有 \(p_n\) 个 \(1\) 和 \(p_n - s\) 个 \(-1\)。考虑在此基础上对序列进行修改使得其满足其他前缀和的要求。
设 \(s_i\) 表示前 \(i\) 个元素的和,那么我们可以发现,若在 \(a_i\) 后插入两个元素 \(-1, 1\),那么 \(s_i\) 和 \(s_i - 1\) 在前缀和集合中均会多出现一次,这启发我们从大到小依次去在序列中插入 \(-1, 1\) 并满足对应的前缀和出现次数的要求。
由于初始序列或为了满足较大的前缀和的要求,当前枚举到的前缀和出现的次数不一定是常数,但是可以通过考虑以上两个因素进行计算。在得到当前前缀和已经出现的次数后,我们便可以得到需要插入的 \(-1, 1\) 的个数,并且每次插入必须插入在当前前缀和出现的位置后。设当前前缀和已经出现了 \(x\) 次,还需要插入 \(y\) 次,那么其方案数为在长度为 \(y\) 的序列中插入 \(x - 1\) 个隔板的方案数,即 \(\dbinom{x + y - 1}{y}\)。
这样我们就可以在 \(O(n)\) 的时间内枚举序列的和,然后在 \(O(n)\) 的时间内计算出对应的方案数,总时间复杂度为 \(O(n^2)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<ValueMatrix> ValueCube;
typedef std::string string;
typedef std::vector<string> StringVector;
typedef std::vector<bool> bitset;
typedef std::vector<bitset> BitMatrix;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::vector<TupleVector> TupleMatrix;
typedef std::set<valueType> ValueSet;
namespace MODINT_WITH_FIXED_MOD {
constexpr valueType MOD = 998244353;
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 T;
std::cin >> T;
for (valueType testcase = 0; testcase < T; ++testcase) {
valueType N;
std::cin >> N;
valueType const M = 2 * N;
ValueVector Fact(M + 1, 0), InvFact(M + 1, 0);
Fact[0] = 1;
for (valueType i = 1; i <= M; ++i)
Fact[i] = mul(Fact[i - 1], i);
InvFact[M] = pow(Fact[M], MOD - 2);
for (valueType i = M - 1; i >= 0; --i)
InvFact[i] = mul(InvFact[i + 1], i + 1);
auto C = [&](valueType n, valueType m) -> valueType {
if (n < m || n < 0 || m < 0)
return 0;
return mul(Fact[n], mul(InvFact[m], InvFact[n - m]));
};
ValueVector F(2 * N + 15, 0);
valueType const offset = N + 5;
for (valueType i = 0; i < N; ++i) {
valueType x;
std::cin >> x;
++F[x + offset];
}
++F[offset];
valueType min = std::numeric_limits<valueType>::max(), max = std::numeric_limits<valueType>::min();
for (valueType i = 0; i < F.size(); ++i) {
if (F[i] > 0) {
min = std::min(min, i);
max = std::max(max, i);
}
}
if (min == max || std::count(F.begin() + min, F.begin() + max + 1, 0) != 0) {
std::cout << 0 << '\n';
continue;
}
valueType ans = 0;
for (valueType s = min; s <= max; ++s) {
ValueVector G(F.size(), 0);
G[max - 1] = F[max] + (max > offset) - (max == s);
for (valueType i = max - 2; i >= min - 1; --i)
G[i] = F[i + 1] - G[i + 1] + (i >= s) + (i >= offset);
if (G[min - 1] != 0)
continue;
valueType product = 1;
for (valueType i = min; i < max; ++i)
Mul(product, C(F[i] - 1, F[i] - G[i]));
Inc(ans, product);
}
std::cout << ans << '\n';
}
std::cout << std::flush;
return 0;
}