Loading

Luogu P11362 [NOIP2024] 遗失的赋值 题解

Link

我觉得真是个 Hard Prob,可能是我计数题做得少想起来特别费劲,也没有找到这条形如长链的关系式的处理方法,卡在了反着算之后。

注意到我们会有大多数合法的方案和少数不合法的情况,正难则反,倒过来做。考虑不合法的情况为:

  • 赋值语句出现矛盾
  • 由一个赋值语句+若干条钦定后的条件语句无法推导出(新的,后面的)另外的赋值语句

我们从后者入手,考虑这样一个简化后的情况,对于一个形如 \(x_1, x_2, \dots, x_{n - 1}, x_n\) 的序列,其中 \(\forall x_i \in [1, v]\)\(x_1, x_n\) 已经由赋值语句钦定的情况下,构造出合法的条件语句的方案数?注意到由于我们不能出现重复定义同一个 \(a_i\) 后继为不同值的不合法情况,所以不满足限制的条件语句形式总如:

\[\begin{aligned} (a_1, b_1) &= (p_1, p_2), &p_1 = x_1 \\ (a_2, b_2) &= (p_2, p_3) \\ &\dots \\ (a_{n - 1}, b_{n - 1}) &= (p_{n - 1}, p_n), &p_n \neq x_n \\ \end{aligned} \]

由于我们钦定了 \(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;
}
posted @ 2025-11-04 20:54  夢回路  阅读(4)  评论(0)    收藏  举报