Luogu P11362 [NOIP2024] 遗失的赋值 题解
我觉得真是个 Hard Prob,可能是我计数题做得少想起来特别费劲,也没有找到这条形如长链的关系式的处理方法,卡在了反着算之后。
注意到我们会有大多数合法的方案和少数不合法的情况,正难则反,倒过来做。考虑不合法的情况为:
- 赋值语句出现矛盾
- 由一个赋值语句+若干条钦定后的条件语句无法推导出(新的,后面的)另外的赋值语句
我们从后者入手,考虑这样一个简化后的情况,对于一个形如 \(x_1, x_2, \dots, x_{n - 1}, x_n\) 的序列,其中 \(\forall x_i \in [1, v]\) 且 \(x_1, x_n\) 已经由赋值语句钦定的情况下,构造出合法的条件语句的方案数?注意到由于我们不能出现重复定义同一个 \(a_i\) 后继为不同值的不合法情况,所以不满足限制的条件语句形式总如:
由于我们钦定了 \(p_1\),又对于任意的 \(p_i, i \in [2, n)\) 都有 \([1, v]\) 这 \(v\) 种取法,同时最后的 \(p_n\) 有 \([1, v)\) 这 \(v - 1\) 种取法,方案数为 \((v^2)^{n - 1}\);总的方案数即 \(a_i, b_i\) 各自有 \(v\) 种取法为 \((v^2)^{n - 1}\),所以合法方案数为 \((v^2)^{n - 1} - v^{n - 2}(v - 1)\)。
考虑这个序列的取值为“开”时的情况,也就是 \(p_1, p_n\) 中至少有一个未被赋值语句钦定。类似于移动区间端点,如果 \(p_1\) 非定值,让 \(x_1, x_2, \dots, x_{n - 1}\) 满足条件语句限制即可,对于 \(p_n\) 未加限制的情况更加简单,因为少了终点的约束,条件语句已经不再重要。
根据这个小区间的启发,我们将题目中的 \(m\) 条赋值语句按照位置排序并拆分出 \(m + 1\) 条子序列,每个子序列就是上面的简化情形,根据乘法原理乘出区间方案数统计答案就做完了。
#include <bits/stdc++.h>
using i64 = long long;
constexpr int N = 1e5 + 7;
constexpr int P = 1e9 + 7;
template <typename T>
T expow(T a, T b) {
T res = 1;
for (; b; b >>= 1) {
if (b & 1)
res = res * a % P;
a = a * a % P;
}
return res;
}
void solve() {
int n, m; i64 v;
std::cin >> n >> m >> v;
std::vector<std::pair<int, i64>> a(m + 1);
for (int i = 1; i <= m; i++) {
std::cin >> a[i].first >> a[i].second;
}
std::sort(a.begin() + 1, a.end());
i64 ans = 1, res = 0;
for (int i = 0; i <= m; i++) {
if (i && i < m && a[i].first == a[i + 1].first) {
if (a[i].second != a[i + 1].second) {
std::cout << "0\n";
return;
} else {
continue;
}
}
if (!i) {
res = expow<i64>(v % P, 2ll * (a[1].first - 1));
} else if (i == m) {
res = expow<i64>(v % P, 2ll * (n - a[m].first));
} else {
res = (expow<i64>(v % P, 2ll * (a[i + 1].first - a[i].first)) - expow<i64>(v % P, (a[i + 1].first - a[i].first - 1)) * (v - 1) % P + P) % P;
}
ans = ans * res % P;
}
std::cout << ans % P << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号