这题在场上卡了我很久,研究了一段时间题解之后觉得非常精妙,所以决定认真写一写题解
第一种方法:
就是题解方法,我们考虑对于数字串的每个数字加入哪一个串,此时第一个串末尾是t1,第二个是t2,当前这个是x
- \(x \neq t1\) && \(x = t2\) 或者 \(x = t1\) && \(x \neq t2\)
对于这种情况我们肯定是把x放在末尾不相等的那个串后面更优,证明也很容易想到 - \(x = t1 = t2\)
这种无论放在哪个后面都是一样的,可以随便放 - \(x \neq t1\) && \(x \neq t2\)
这种情况比较麻烦,题解里的方法是用next数组记录i后面第一次出现这个数字的位置,选择把x放在末尾数字的next更小的串后面,证明比较复杂,但是从直观的角度来看,我们在做完这次操作之后,结果一定是一个以x结尾,一个以其他的结尾,那么要想让下次尽量的不要出现和其他的那个相等导致没有贡献的情况,则我们希望下次出现这个数字尽量的晚,这样连起来的可能性更小
基于题解,可以有一个\(O(n)\)的做法,但是这个做法需要一个next数组辅助,实际上可以更加简化
那就是对于第三种情况,只考虑当前这个下一个就够了,即考虑\(X_{i+1}\)和\(t1, t2\)之间的关系,如果和t1相等,就把x连接到t1,否则连接到t2。对于这个方法,简单的理解就是,基于上面我们的推断,如果和t1或者t2相等,那么显然next更小一些,那为什么当都不相等的时候,随意选择就可以了呢,这是因为这说明了 \(x_i,x_{i+1}\) 和t1, t2都不相等,那么显然\(x_{i+1}\)是一定能产生贡献的,\(x_{i+2}\)无论如何也一定能产生贡献。
但是还有一种更简单的方法,就是不需要任何辅助,对于第三种情况,产生的结果一定是一个以x结尾,另一个以t1或者t2结尾,那么它既可以是t1,也可以是t2,所以无论后面连接的是什么,它都一定是一个不同的数字,所以把它变成0,一定会让后面产生贡献,代码如下
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
int t1 = 0, t2 = 0, ans = n;
for (int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
if (x == t1) {
if (x == t2) ans--;
else t2 = x;
}
else {
if (t2 != x && t2 != t1) t2 = 0;
t1 = x;
}
}
printf("%d\n", ans);
return 0;
}```