CSPS-2024补题笔记--染色

T3 染色

题目:染色(color)

首先, 我们可以发现, 只有当颜色相同的时候我们的当前元素才会产生奉献, 这启示我们可以在后面用一个桶(\(a_i\) 的规模不超过 \(10 ^ 6\))来记录上一次当前值出现的位置。

既然我们现在有对于值相同情况时的记录了, 然后我们可以发现, 对于每一个位置, 我们其实产生影响的只有上一个和当前同色并且是\(lst_{a_i}\)的东西, 然后我们需要考虑如果说当前换了和前面比的话会不会现在更劣, 但是在后面逆转为更好的呢?

对于这一个问题, 我们可以先放在一旁, 首先先开始写公式

首先我们可以知道, 如果说是下图的情况, 我们中间的奉献就是中间相同值的一个和, 对于这个东西, 我们可以提前预处理出来中间产生的奉献, 此处记作\(s_i\)为到当前位置这个相邻元素产生的奉献的前缀和, 以便于后面进行区间的计算

图1

然后我们就可以发现, 对于前面所说的当前情况更劣然后转换为好的话, 那我们后面的值是在前面的值上面进行计算的, 而前面的值越大, 对于后面我们的转移会更有利。

而对于把上一个和自己相同值的转换为和自己同色的话, 我们需要把关于那个值所产生的奉献给删除, 然后再加上当前这段区间的值(因为对于上一个值的前面的不同色的, 我们已经存在动规数组\(f_i\)中了, 不需要重新计算)

这样的话, 我们就有dp方程:

\[f_i = max(f_{i - 1}, f_{lst_{a_i} + 1} + a_i + s_i - s_{lst_{a_i} + 1}) \]

同时, 在每次计算完当前的值后, 就可以把当前的\(lst_{a_i}\)更新为当前位置, 因为每一次我们所需要的都是最近一个的同值位置

然后这个公式的解读如下:

  • 对于\(max\), 我们需要把当前换和不换进行比较, 选择更优的替换, 这很容易理解
  • 对于$ f_{lst_{a_i} + 1}\(, 就是在上一个同值的元素后一个位置, 也就是要包含\)lst_{a_i}$前面的结果
  • 对于\(a_i\), 就是当前会产生的奉献
  • 对于\(s_i - s_{lst_{a_i} + 1}\), 就是对于当前值和上一个相同值的中间所产生的奉献区间和

这样的话, 这道题我们就做完了, 我觉得这道题给我的启示如下

  • 首先是我们需要考虑当前值需要从哪一些地方转移, 会影响什么东西
  • 其次, 我们可以对于没有修改的, 然后需要查询的值去进行预处理或者说对于每一个位置去处理好, 这样可以把复杂度优化很多
  • 对于把上一个元素给更改啊这样会对前面的奉献所产生的影响, 需要考虑进入方程当中

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], lst[N];
long long s[N];
long long f[N];

void solve() {
	memset(f, 0, sizeof(f));
	memset(lst, 0, sizeof(lst));
	memset(s, 0, sizeof(s));
	int n;
	cin >> n;
	for(int i = 1;i <= n;i++) {
		cin >> a[i];
	}
	for(int i = 2;i <= n;i++) {
		s[i] = ((a[i] == a[i - 1]) ? s[i - 1] + a[i] : s[i - 1]);
	}
	for(int i = 1;i <= n;i++) {
		f[i] = f[i - 1];
		if(lst[a[i]]) {
			f[i] = max({f[i], f[lst[a[i]] + 1] + a[i] + s[i] - s[lst[a[i]] + 1]});
		}
		lst[a[i]] = i;
	}
	cout << f[n] << '\n';
}

signed main() {
	ios::sync_with_stdio(0); cin.tie(0);
	int T;
	cin >> T;
	while(T--) {
		solve();
	}

	return 0;
}
posted @ 2026-09-08 13:12  Zhenggeek  阅读(4)  评论(0)    收藏  举报