html

[CCPC 2024 重庆站] Median Replacement 题解

题意

给定一个长为 \(n\) 的整数序列 \(a_1,a_2,\dots,a_n\),你需要对 \(a\) 进行若干次如下操作,使得 \(a\) 中所有数均相等:

  • 选择一段长为大于 \(1\) 的奇数的区间 \(a_l,a_{l+1},\ldots,a_r\),并将此区间内的所有数均替换为它们的中位数。

设最终 \(a_1=a_2=\ldots=a_n=x\),我们定义序列 \(a\) 的值为 \(x\) 的最大值。

请你求出所有满足 \(\forall\ 1\le i\le n\),\(l_i\le a_i\le r_i\) 的整数序列 \(a\) 的值之和。

由于答案可能很大,请对 \(10^9+7\) 取模。

思路

不难发现,答案 \(\text{ans} = \sum x \times \operatorname{cnt}[i=x]\) ,其中 \(\operatorname{cnt}[i=x]\) 为整个区间被覆盖成 \(x\) 的方案数。

进而可以转化成 \(\text{ans} = \sum \operatorname{cnt}[i\geq x]\) ,其中 \(\operatorname{cnt}[i\geq x]\) 为整个区间被覆盖成大于等于 \(x\) 的方案数。

我们继续考虑什么样子的情况是合法的。

如果存在连续三个数,其中两个数大于等于 \(x\),那么整个序列能被全部被变成大于等于 \(x\) 的数 ,也就是:

\[\exists 正整数 i,j \in [1,n],1\leq|i-j|\leq2,\quad有a_i \geq x, a_j \geq x \]

我们构造考虑新的数列 \(b\)

\[b_i= \begin{cases} 0, & a_i < x \\ 1, & a_i \geq x \\ \end{cases} \]

那么合法情况可以转化为:

\[\exists 正整数 i,j \in [1,n],1\leq|i-j|\leq2,\quad有b_i = b_j = 1 \]

但是我们发现求 \(\operatorname{cnt}[i\geq x]\) 并不好求,但是我们发现求总方案数减去\(\operatorname{cnt}[i < x]\) 是好求的。

那我们枚举阈值\(x\), 设 \(dp_{i,0/1,0/1}\) 为前 \(i\) 个数中最后两位在 \(b\) 中的值分别为 \(0/1,0/1\)的方案数,显然有转移:

\[dp_{i,0}=(dp_{i-1,0}+dp_{i-1,1})A_i,\\ dp_{i,1}=dp_{i-1,2}A_i,\\ dp_{i,2}=dp_{i-1,0}B_i. \]

其中

\[A_i = \operatorname{cnt}[a_i < x], a_i\in[l_i,r_i]\\ B_i = \operatorname{cnt}[a_i \geq x], a_i\in[l_i,r_i]. \]

复杂度\(\Theta(n \times V)\) ,\(V\) 为值域。

我们接着考虑如何优化。

我们注意到 \(x\) 的取值在一段\([L,R]\) 中时,方案数一定是一个常数或者一个一次函数。

有

\[A_i(x)= \begin{cases} 0 , & x < l_i, \\ x - l_i , & l_i \le x \le r_i, \\ r_i - l_i+1 , & x > r_i, \end{cases} \]

以及

\[B_i(x) = \begin{cases} r_i - l_i + 1, & x < l_i, \\ r_i + 1 - x, & l_i \le x \le r_i, \\ 0, & x >r_i. \end{cases} \]

因此,将所有 \(1,\quad 10^9+1,\quad l_i,\quad r_i+1\) 排序去重,相邻两个端点构成一个整数区间

\[[L,R]=[\text{vec}_j,\text{vec}_{j+1}-1]. \]

每一段的贡献即为

\[\begin{aligned} \sum_{x=L}^R(\text{tot}-F(x)) &=\text{tot}(R-L+1) -\sum_{x=L}^R\sum_{k=0}^n c_kx^k\\ &=\text{tot}(R-L+1) -\sum_{k=0}^n c_k\left(S_k(R)-S_k(L-1)\right), \end{aligned} \]

其中

\[F(x) 是一个多项式, F(x) = dp_{n,0} + dp_{n, 1} + dp_{n, 2}\\ tot 是总方案数\\ c_k为多项式每项系数\\ S_k(t)=\sum_{u=1}^t u^k\\ \]

对于 \(S_k(t)=\sum_{u=1}^t u^k\) 使用拉格朗日插值即可

Code:

\(\text{C++ Code:}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int MAXN = 200 + 5;
constexpr int MOD = 1e9 + 7;
int n;
int l[MAXN], r[MAXN];
int frac[MAXN], ifrac[MAXN];
int inv[MAXN];
int y[MAXN][MAXN];
void Mod(int& x) {
	if (x >= MOD) x -= MOD;
	if (x < 0) x += MOD;
}

struct Poly: vector<int> {
	Poly(int sz, int val = 0) : vector<int>(sz, val) {}
	Poly(vector<int> c = {}) : vector<int>(c) {}
	Poly operator + (const Poly& b) const {
		Poly res(max(size(), b.size()));
		for (int i = 0; i < (int)size(); ++i) Mod(res[i] += (*this)[i]);
		for (int i = 0; i < (int)b.size(); ++i) Mod(res[i] += b[i]);
		return res;
	}
	Poly operator * (const Poly& b) const {
		Poly res(size() + b.size() - 1);
		for (int i = 0; i < (int)size(); ++i)
			for (int j = 0; j < (int)b.size(); ++j)
				Mod(res[i + j] += (*this)[i] * b[j] % MOD);
		return res;
	}
};
Poly dp[MAXN][4];
int qpow(int base, int x) {
	int res = 1;
	while(x) {
		if (x & 1)
			res = res * base % MOD;
		base = base *  base % MOD, x >>= 1;
	}
	return res;
}
void init() {
	frac[0] = 1;
	for (int i = 1; i < MAXN; ++i) 
		frac[i] = frac[i - 1] * i % MOD;
	ifrac[MAXN - 1] = qpow(frac[MAXN - 1], MOD - 2);
	for (int i = MAXN - 2; i >= 0;--i) 
		ifrac[i] = ifrac[i + 1] * (i + 1) % MOD;
	for (int i = 1; i < MAXN; ++i)
		y[i][0] = 1;
	for (int i = 0; i < MAXN; ++i)
		for (int j = 1; j < MAXN; ++j)
			y[i][j] = (y[i][j - 1] + qpow(j, i)) % MOD;
}
Poly calc(int l, int r, int L, int R, int c) {
	Poly res(2);
	if (c) {
		if (l <= L && R <= r) res[0] = r + 1, res[1] = -1;
		if (R < l) res[0] = r - l + 1, res[1] = 0;
		if (r < L) res[0] = 0, res[1] = 0;
	}
	else {
		if (l <= L && R <= r) res[0] = -l, res[1] = 1;
		else if (r < L) res[0] = r - l + 1, res[1] = 0;
		else if (R < l) res[0] = 0, res[1] = 0;
	}
	return res;
}
int calc_power_sum(int x, int k) {
	if (x <= n + 2)
		return y[k][x];
	int mul = 1;
	for (int i = 1; i <= n + 2; ++i)
		(mul *= (x - i)) %= MOD;
	int res = 0;
	for (int i = 1; i <= n + 2; ++i) {
		int tmp = y[k][i] * mul % MOD * qpow(x - i , MOD - 2) % MOD * ifrac[i - 1] % MOD * ifrac[n + 2 - i] % MOD;
		(((res += qpow(-1, (n + 2 - i) & 1) * tmp) %= MOD) += MOD) %= MOD;
	}
	return res;
}

void solve() {
	cin >> n;
	vector<int> vec;
	vec.reserve(2 * n + 2);
	vec.push_back(1), vec.push_back(1e9 + 1);
	for (int i = 1; i <= n; ++i)
		cin >> l[i], vec.push_back(l[i]);
	for (int i = 1; i <= n; ++i)
		cin >> r[i], vec.push_back(r[i] + 1);
	sort(vec.begin(), vec.end());
	vec.erase(unique(vec.begin(), vec.end()), vec.end());
	int tot = 1;
	for (int i = 1; i <= n; ++i) 
		(tot *= (r[i] - l[i] + 1)) %= MOD;
	int res = 0;
	for (int i = 0; i + 1 < (int)vec.size(); ++i) {
		int L = vec[i], R = vec[i + 1] - 1;
		dp[2][0] = calc(l[1], r[1], L, R, 0) * calc(l[2], r[2], L, R, 0);
		dp[2][1] = calc(l[1], r[1], L, R, 1) * calc(l[2], r[2], L, R, 0);
		dp[2][2] = calc(l[1], r[1], L, R, 0) * calc(l[2], r[2], L, R, 1);
		for (int j = 3; j <= n; ++j) {
			dp[j][0] = (dp[j - 1][0] + dp[j - 1][1]) * calc(l[j], r[j], L, R, 0);
			dp[j][1] = dp[j - 1][2] * calc(l[j], r[j], L, R, 0);
			dp[j][2] = dp[j - 1][0] * calc(l[j], r[j], L, R, 1);
		}
		auto F = dp[n][0] + dp[n][1] + dp[n][2];
		int ans = tot * (R - L + 1) % MOD;
		for (int i = 0; i < (int)F.size(); ++i) {
			ans -= F[i] * (calc_power_sum(R, i) - calc_power_sum(L - 1, i) + MOD) % MOD;
			(ans += MOD) %= MOD;
		}
		(res += ans) %= MOD;
	}
	cout << res<< '\n';
}
signed main() {
#ifndef DEBUG
	ios::sync_with_stdio(0), cin.tie(0);
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);
#else
#endif
	init();
	int T;
	cin >> T;
	while (T--)
		solve();
	return 0;
}
posted @ 2026-10-27 17:57  孤独的Bochi  阅读(6)  评论(0)    收藏  举报
​