P11233 [CSP-S 2024] 染色
题面:
题目描述
给定一个长度为 n n n 的正整数数组 A A A,其中所有数从左至右排成一排。
你需要将 A A A 中的每个数染成红色或蓝色之一,然后按如下方式计算最终得分:
设 C C C 为长度为 n n n 的整数数组,对于 A A A 中的每个数 A i A_i Ai( 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n):
- 如果 A i A_i Ai 左侧没有与其同色的数,则令 C i = 0 C_i = 0 Ci=0。
- 否则,记其左侧与其最靠近的同色数为 A j A_j Aj,若 A i = A j A_i = A_j Ai=Aj,则令 C i = A i C_i = A_i Ci=Ai,否则令 C i = 0 C_i = 0 Ci=0。
你的最终得分为 C C C 中所有整数的和,即 ∑ i = 1 n C i \sum \limits_{i=1}^n C_i i=1∑nCi。你需要最大化最终得分,请求出最终得分的最大值。
输入格式
本题有多组测试数据。
输入的第一行包含一个正整数 T T T,表示数据组数。
接下来包含 T T T 组数据,每组数据的格式如下:
第一行包含一个正整数 n n n,表示数组长度。
第二行包含 n n n 个正整数 A 1 , A 2 , … , A n A_1, A_2, \dots, A_n A1,A2,…,An,表示数组 A A A 中的元素。
输出格式
对于每组数据:输出一行包含一个非负整数,表示最终得分的最大可能值。
样例 #1
样例输入 #1
3 3 1 2 1 4 1 2 3 4 8 3 5 2 5 1 2 1 4
样例输出 #1
1 0 8
提示
【样例 1 解释】
对于第一组数据,以下为三种可能的染色方案:
- 将 A 1 , A 2 A_1, A_2 A1,A2 染成红色,将 A 3 A_3 A3 染成蓝色( 1 2 1 \red{1}\red{2}\blue{1} 121),其得分计算方式如下:
- 对于 A 1 A_1 A1,由于其左侧没有红色的数,所以 C 1 = 0 C_1 = 0 C1=0。
- 对于 A 2 A_2 A2,其左侧与其最靠近的红色数为 A 1 A_1 A1。由于 A 1 ≠ A 2 A_1 \neq A_2 A1=A2,所以 C 2 = 0 C_2 = 0 C2=0。
- 对于 A 3 A_3 A3,由于其左侧没有蓝色的数,所以 C 3 = 0 C_3 = 0 C3=0。
该方案最终得分为 C 1 + C 2 + C 3 = 0 C_1 + C_2 + C_3 = 0 C1+C2+C3=0。
- 将 A 1 , A 2 , A 3 A_1, A_2, A_3 A1,A2,A3 全部染成红色( 121 \red{121} 121),其得分计算方式如下:
- 对于 A 1 A_1 A1,由于其左侧没有红色的数,所以 C 1 = 0 C_1 = 0 C1=0。
- 对于 A 2 A_2 A2,其左侧与其最靠近的红色数为 A 1 A_1 A1。由于 A 1 ≠ A 2 A_1 \neq A_2 A1=A2,所以 C 2 = 0 C_2 = 0 C2=0。
- 对于 A 3 A_3 A3,其左侧与其最靠近的红色数为 A 2 A_2 A2。由于 A 2 ≠ A 3 A_2 \neq A_3 A2=A3,所以 C 3 = 0 C_3 = 0 C3=0。
该方案最终得分为 C 1 + C 2 + C 3 = 0 C_1 + C_2 + C_3 = 0 C1+C2+C3=0。
- 将 A 1 , A 3 A_1, A_3 A1,A3 染成红色,将 A 2 A_2 A2 染成蓝色( 1 2 1 \red{1}\blue{2}\red{1} 121),其得分计算方式如下:
- 对于 A 1 A_1 A1,由于其左侧没有红色的数,所以 C 1 = 0 C_1 = 0 C1=0。
- 对于 A 2 A_2 A2,由于其左侧没有蓝色的数,所以 C 2 = 0 C_2 = 0 C2=0。
- 对于 A 3 A_3 A3,其左侧与其最靠近的红色数为 A 1 A_1 A1。由于 A 1 = A 3 A_1 = A_3 A1=A3,所以 C 3 = A 3 = 1 C_3 = A_3 = 1 C3=A3=1。
该方案最终得分为 C 1 + C 2 + C 3 = 1 C_1 + C_2 + C_3 = 1 C1+C2+C3=1。可以证明,没有染色方案使得最终得分大于 1 1 1。
对于第二组数据,可以证明,任何染色方案的最终得分都是 0 0 0。
对于第三组数据,一种最优的染色方案为将 A 1 , A 2 , A 4 , A 5 , A 7 A_1, A_2, A_4, A_5, A_7 A1,A2,A4,A5,A7 染为红色,将 A 3 , A 6 , A 8 A_3, A_6, A_8 A3,A6,A8 染为蓝色( 35 2 51 2 1 4 \red{35}\blue{2}\red{51}\blue{2}\red{1}\blue{4} 35251214),其对应 C = [ 0 , 0 , 0 , 5 , 0 , 1 , 2 , 0 ] C = [0, 0, 0, 5, 0, 1, 2, 0] C=[0,0,0,5,0,1,2,0],最终得分为 8 8 8。
【样例 2】
见选手目录下的 color/color2.in 与 color/color2.ans。
【数据范围】
对于所有测试数据,保证: 1 ≤ T ≤ 10 1\leq T\leq 10 1≤T≤10, 2 ≤ n ≤ 2 × 1 0 5 2\leq n\leq 2\times 10^5 2≤n≤2×105, 1 ≤ A i ≤ 1 0 6 1\leq A_i\leq 10^6 1≤Ai≤106。
测试点 n n n A i A_i Ai 1 ∼ 4 1\sim 4 1∼4 ≤ 15 \leq 15 ≤15 ≤ 15 \leq 15 ≤15 5 ∼ 7 5\sim 7 5∼7 ≤ 1 0 2 \leq 10^2 ≤102 ≤ 1 0 2 \leq 10^2 ≤102 8 ∼ 10 8\sim 10 8∼10 ≤ 2000 \leq 2000 ≤2000 ≤ 2000 \leq 2000 ≤2000 11 , 12 11,12 11,12 ≤ 2 × 1 0 4 \leq 2\times 10^4 ≤2×104 ≤ 1 0 6 \leq 10^6 ≤106 13 ∼ 15 13\sim 15 13∼15 ≤ 2 × 1 0 5 \leq 2\times 10^5 ≤2×105 ≤ 10 \leq 10 ≤10 16 ∼ 20 16\sim 20 16∼20 ≤ 2 × 1 0 5 \leq 2\times 10^5 ≤2×105 ≤ 1 0 6 \leq 10^6 ≤106
定义 f i f_i fi 为枚举到 i i i 时的答案, s i s_i si 为相邻两项相同的前缀和。
有转移:
f i = max { f j + [ a i = a j ] × a i + s i − 1 − s j + 1 } f_i = \max\{f_j + [a_i = a_j] \times a_i + s_{i - 1} - s_{j + 1}\} fi=max{fj+[ai=aj]×ai+si−1−sj+1}
发现只有相同元素会产生贡献,于是用桶维护相同元素的最优决策点。
即: b u c a i = max { f j − s j + 1 } buc_{a_i} = \max\{f_j - s_{j + 1}\} bucai=max{fj−sj+1}
有转移:
f i = max { b u c a i + a i + s i − 1 } f_i = \max\{buc_{a_i} + a_i + s_{i - 1}\} fi=max{bucai+ai+si−1}
(虽然但是,我用的是一种更难理解,但是更好写的代码)
AC-code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {int x = 0, w = 1;char ch = 0;while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9') {x = x * 10 + (ch - '0');ch = getchar();}return x * w;
}
void wt(int x) {static int sta[35];int f = 1;if(x < 0) f = -1,x *= f;int top = 0;do {sta[top++] = x % 10, x /= 10;} while (x);if(f == -1) putchar('-');while (top) putchar(sta[--top] + 48);
}namespace work{
const int N = 2e5+5,M = 1e6 + 5;
int n,a[M],s[M],f[M],t[M];
void solve() {memset(s,0,sizeof(s));memset(t,0,sizeof(t));memset(f,0,sizeof(f));n = rd();for(int i = 1;i<=n;i++) a[i] = rd();for(int i = 2;i<=n;i++) s[i] = s[i - 1] + (a[i] == a[i - 1]) * a[i];for(int i = 1;i<=n;i++) {f[i] = f[i - 1];if(t[a[i]]) f[i] = max(f[i],f[t[a[i]] + 1] + a[i] + s[i] - s[t[a[i]] + 1]);t[a[i]] = i;}wt(f[n]);putchar('\n');
}}signed main() {int T = rd();while(T--) work::solve();return 0;
}