题解:P17317 [KismetOI 2026 I] 作弊

感觉思路还是很顺畅的。

\(p\) 固定且 \(n>k\),不妨令 \(w_i=D-p_i\),那么修改操作带来的增量就是 \(w\) 上的最大权独立集。序列上最大权独立集有简单 DP:令 \(f_i\) 表示 \(w[i..n]\) 的最大权独立集,则转移为 \(f_i=\max(f_{i+1},f_{i+2}+D-p_i)\)

倒序考虑 \(p_n,\cdots,p_1\),考察我们需要记录什么信息。对于前缀和最大值,容易想到对于每个 \(i\) 记录 \(h_i=\max\limits_{j=i-1}^k\sum\limits_{t=i}^jp_t\),那么转移就是 \(h_i=\max(h_{i+1}+p_i,0)\)。对于修改后 \(p\) 的总和,可以想到直接记录 \(f_i\)

显然有 \(0\leq h_i\leq kV\),可以接受。但是 \(f_i\) 可以达到 \(\mathcal{O}(n(D+V))\) 量级,显然不能承受。考虑转而记录差分数组 \(d_i=f_i-f_{i+1}=\max(D-p_i-d_{i+1},0)\),此时 \(0\leq d_i\leq D+V\),范围就大大缩小了。

下面讨论具体的 DP。

\(n>k\)

固定 \(p\) 时的最终答案为 \(g_{h_1}\sum\limits_{i=1}^n(p_i+d_i)\)

对于 \(i>k\),设计 DP:令 \(sum_{i,d}\) 表示考虑 \(p[i..n]\)\(d_i=d\),此时所有方案的 \(\sum(p_i+d_i)\) 之和,\(cnt_{i,d}\) 表示方案数。转移时枚举 \(p_i\) 可能的取值,设 \(d'=\max(D-p_i-d,0)\)

\[\begin{align*} cnt_{i,d'}&\gets cnt_{i+1,d}\\ sum_{i,d'}&\gets sum_{i+1,d}+cnt_{i+1,d}(p_i+d') \end{align*} \]

对于 \(i\leq k\),给 DP 数组添加一维变成 \((i,h,d)\),表示 \(h_i=h\) 即可。转移是类似的。

最终答案即为

\[\sum_{h=0}^{kV}\sum_{d=0}^{D+V}g_h{sum}_{1,h,d} \]

\(n\leq k\)

\(dp_{i,h}\) 表示考虑 \(p[i..n]\)\(h_i=h\),此时的方案数。转移和前面类似。

最终答案即为

\[\sum_{h=0}^{kV}g_hdp_{1,h} \]


这样做时间复杂度为 \(\mathcal{O}(nV^2+k^2V^3)\),可以通过本题。

主要代码
int n, k, D, V, p[MAXN], g[MAXK * MAXV];

mint solve1() {
	vector<mint> dp(k * V + 1, 0), ndp(k * V + 1);
	dp[0] = 1;
	for (int i = n; i; --i) {
		fill(ndp.begin(), ndp.end(), 0);
		for (int h = 0; h <= k * V; ++h) {
			if (!dp[h].val()) continue;
			int l = p[i] == -7912 ? -V : p[i], r = p[i] == -7912 ? V : p[i];
			for (int x = l; x <= r; ++x) ndp[max(h + x, 0)] += dp[h];
		}
		ndp.swap(dp);
	}
	
	mint res = 0;
	for (int h = 0; h <= k * V; ++h) res += g[h] * dp[h];
	return res;
}

mint solve2() {
	vector<pair<mint, mint>> dp1(D + V + 1, {0, 0}), ndp1(D + V + 1);
	dp1[0] = {1, 0};
	for (int i = n; i > k; --i) {
		fill(ndp1.begin(), ndp1.end(), pair<mint, mint>{0, 0});
		for (int d = 0; d <= D + V; ++d) {
			auto [cnt, sum] = dp1[d];
			if (!cnt.val()) continue;
			int l = p[i] == -7912 ? -V : p[i], r = p[i] == -7912 ? V : p[i];
			for (int x = l; x <= r; ++x) {
				int nd = max(D - x - d, 0);
				ndp1[nd].first += cnt;
				ndp1[nd].second += sum + cnt * (x + nd);
			}
		}
		ndp1.swap(dp1);
	}

	vector<vector<pair<mint, mint>>> dp2(k * V + 1), ndp2(k * V + 1);
	for (auto &vec : dp2) vec.resize(D + V + 1, {0, 0});
	for (auto &vec : ndp2) vec.resize(D + V + 1, {0, 0});
	dp2[0].swap(dp1);
	for (int i = k; i; --i) {
		for (auto &vec : ndp2) fill(vec.begin(), vec.end(), pair<mint, mint>{0, 0});
		for (int h = 0; h <= k * V; ++h) {
			for (int d = 0; d <= D + V; ++d) {
				auto [cnt, sum] = dp2[h][d];
				if (!cnt.val()) continue;
				int l = p[i] == -7912 ? -V : p[i], r = p[i] == -7912 ? V : p[i];
				for (int x = l; x <= r; ++x) {
					int nh = max(h + x, 0), nd = max(D - x - d, 0);
					ndp2[nh][nd].first += cnt;
					ndp2[nh][nd].second += sum + cnt * (x + nd);
				}
			}
		}
		ndp2.swap(dp2);
	}

	mint res = 0;
	for (int h = 0; h <= k * V; ++h) {
		for (int d = 0; d <= D + V; ++d) {
			res += g[h] * dp2[h][d].second;
		}
	}
	return res;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n >> k >> D >> V;
	for (int i = 1; i <= n; ++i) cin >> p[i];
	for (int i = 0; i <= k * V; ++i) cin >> g[i];
	cout << (n <= k ? solve1() : solve2());
	return 0;
}

Bonus

考虑一个瓶颈在于 \(p_i\) 未确定时需要暴力枚举 \(p_i\in[-V,V]\)

观察 \(n>k\) 的 DP 的第一部分,不难发现我们可以直接分讨 \(p_i\)\(D-d\) 的大小关系,然后差分做区间加。

对于第二部分,同样可以大力分讨 \(p_i\)\(D-d,-h\) 的四种大小关系,做三个方向上的差分。

这样时间复杂度就优化到了 \(\mathcal{O}(nV+k^2V^2)\)

当然了,这个优化是平凡的。

主要代码
mint solve2() {
	vector<pair<mint, mint>> dp1(D + V + 1, {0, 0}), df(D + V + 1, {0, 0});
	dp1[0] = {1, 0};
	for (int i = n; i > k; --i) {
		fill(df.begin(), df.end(), pair<mint, mint>{0, 0});
		auto add = [&](int l, int r, mint cnt, mint sum) {
			df[l].first += cnt;
			df[l].second += sum;
			if (r == df.size() - 1) return;
			df[r + 1].first -= cnt;
			df[r + 1].second -= sum;
		};

		for (int d = 0; d <= D + V; ++d) {
			auto [cnt, sum] = dp1[d];
			if (!cnt.val()) continue;
			if (p[i] != -7912) {
				int nd = max(D - p[i] - d, 0);
				add(nd, nd, cnt, sum + cnt * (p[i] + nd));
			} else {
				if (D - d + 1 <= V) {
					int l = max(D - d + 1, -V), r = V, len = r - l + 1;
					add(0, 0, cnt * len, sum * len + cnt * ((l + r) * len >> 1));
				}
				if (-V <= D - d) {
					int l = -V, r = min(D - d, V);
					add(D - d - r, D - d - l, cnt, sum + cnt * (D - d));
				}
			}
		}

		for (int d = 0; d <= D + V; ++d) {
			if (d) {
				df[d].first += df[d - 1].first;
				df[d].second += df[d - 1].second;
			}
		}
		df.swap(dp1);
	}

	vector<vector<pair<mint, mint>>> dp2(k * V + 1), ndp2(k * V + 1);
	vector<vector<pair<mint, mint>>> df1(k * V + 1), df2(D + V + 1), df3(k * V + D + V + 1);
	for (auto &vec : df1) vec.resize(D + V + 1, {0, 0});
	for (auto &vec : df2) vec.resize(k * V + 1, {0, 0});
	for (auto &vec : df3) vec.resize(k * V + 1, {0, 0});
	for (auto &vec : dp2) vec.resize(D + V + 1, {0, 0});
	for (auto &vec : ndp2) vec.resize(D + V + 1, {0, 0});
	dp2[0].swap(dp1);
	for (int i = k; i; --i) {
		for (auto &vec : df1) fill(vec.begin(), vec.end(), pair<mint, mint>{0, 0});
		for (auto &vec : df2) fill(vec.begin(), vec.end(), pair<mint, mint>{0, 0});
		for (auto &vec : df3) fill(vec.begin(), vec.end(), pair<mint, mint>{0, 0});
		auto add = [&](vector<pair<mint, mint>> &df, int l, int r, mint cnt, mint sum) {
			df[l].first += cnt;
			df[l].second += sum;
			if (r == df.size() - 1) return;
			df[r + 1].first -= cnt;
			df[r + 1].second -= sum;
		};

		for (int h = 0; h <= k * V; ++h) {
			for (int d = 0; d <= D + V; ++d) {
				auto [cnt, sum] = dp2[h][d];
				if (!cnt.val()) continue;
				if (p[i] != -7912) {
					int nh = max(h + p[i], 0), nd = max(D - p[i] - d, 0);
					add(df1[nh], nd, nd, cnt, sum + cnt * (p[i] + nd));
				} else {
					// x >= D - d + 1
					// x <= D - d
					// x >= -h + 1
					// x <= -h
					int l = max(D - d + 1, -V), r = min(-h, V);
					if (l <= r) {
						int len = r - l + 1;
						add(df1[0], 0, 0, cnt * len, sum * len + cnt * ((l + r) * len >> 1));
					}

					l = max({D - d + 1, -h + 1, -V});
					r = V;
					if (l <= r) {
						add(df2[0], h + l, h + r, cnt, 0);
						// sum + cnt * l ~ sum + cnt * r
						add(df2[0], h + l, h + l, 0, sum + cnt * l);
						if (l < r) add(df2[0], h + l + 1, h + r, 0, cnt);
						if (h + r + 1 <= k * V) add(df2[0], h + r + 1, h + r + 1, 0, -sum - cnt * r);
					}

					l = -V;
					r = min({D - d, -h, V});
					if (l <= r) add(df1[0], D - d - r, D - d - l, cnt, sum + cnt * (D - d));

					l = max(-h + 1, -V);
					r = min(D - d, V);
					if (l <= r) add(df3[D - d + h], h + l, h + r, cnt, sum + cnt * (D - d));
				}
			}
		}

		for (auto &vec : ndp2) fill(vec.begin(), vec.end(), pair<mint, mint>{0, 0});
		for (int d = 0; d <= D + V; ++d) {
			for (int h = 0; h <= k * V; ++h) {
				if (h) df2[d][h].second += df2[d][h - 1].second;
			}
		}
		for (int h = 0; h <= k * V; ++h) {
			for (int d = 0; d <= D + V; ++d) {
				int x = d + h;
				if (d) {
					df1[h][d].first += df1[h][d - 1].first;
					df1[h][d].second += df1[h][d - 1].second;
				}
				if (h) {
					df2[d][h].first += df2[d][h - 1].first;
					df2[d][h].second += df2[d][h - 1].second;
					df3[x][h].first += df3[x][h - 1].first;
					df3[x][h].second += df3[x][h - 1].second;
				}
				ndp2[h][d].first += df1[h][d].first + df2[d][h].first + df3[x][h].first;
				ndp2[h][d].second += df1[h][d].second + df2[d][h].second + df3[x][h].second;
			}
		}
		ndp2.swap(dp2);
	}

	mint res = 0;
	for (int h = 0; h <= k * V; ++h) {
		for (int d = 0; d <= D + V; ++d) {
			res += g[h] * dp2[h][d].second;
		}
	}
	return res;
}
posted @ 2026-08-25 21:43  P2441M  阅读(3)  评论(0)    收藏  举报