Constant Palindrome Sum
思路
由于每一个数字的取值范围是([1, k]),所以对与每一对数字的和的取值应该在([2, 2k])。
对于每一对数字我们可以选择改变一个、改变两个或者一个都不改变。
-
改变一个的时候,数值区域可以变成([min(a, b) + 1, max(a, b) + k])
-
改变两个的时候,数值区域可以变成([2, min(a, b)]),和([max(a, b) + k + 1, 2k])。
-
一个都不变,其值只有可能是(a + b)。
我们定义三个数组
-
1、num数组,记录的是下标为 ((a + b))的数对的个数。
-
2、change1数组,记录的是改变一个数字的时候可取的区域相应的操作数,这里为了降低复杂度所以采用差分的形式,大致操作如下
(change1[min(a, b) + 1]++)
(change1[max(a, b) + k + 1]--) -
3、change2数组,记录的是改变两个数字对应的操作次数,其对应对change2数组的操作如下
(change2[2] += 2,change2[min(a, b) + 1] -= 2)
(change2[max(a, b) + k + 1] += 2,change2[2k + 1] -= 2)
所以最后的答案就是当前答案操作一次的数量加上操作两次的数量减去当前答案的数值的数量
也就是 (change1[i] + change2[i] - num[i])
减去 (num[i]) 的操作相信大家应该明白,就是在
([min(a, b) + 1, max(a, b) + k]) 中包括了 (a + b)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5 + 10;
const int INF = 0x3f3f3f3f;
int a[N], num[N], change1[N], change2[N], n, m;
int main() {
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) {
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 0; i <= 2 * m + 5; i++)
num[i] = change1[i] = change2[i] = 0;
//所有和的可能性[2, 2m]
for(int i = 1; i <= n / 2; i++) {
num[a[i] + a[n - i + 1]]++;
int l = min(a[i], a[n - i + 1]) + 1;
int r = max(a[i], a[n - i + 1]) + m;
//[l, r]只要改变一个数
change1[l]++, change1[r + 1]--;
//[2, l - 1] 和 [r + 1, 2m]改变两个数。
change2[2] += 2, change2[l]-= 2;
change2[r + 1] += 2;
}
for(int i = 3; i <= 2 * m + 5; i++) {
// num[i] += num[i - 1];
change1[i] += change1[i - 1];
change2[i] += change2[i - 1];
}
// for(int i = 2; i <= 2 * m; i++) {
// printf("%d ", num[i]);
// }
// puts("");
// for(int i = 2; i <= 2 * m; i++) {
// printf("%d ", change1[i]);
// }
// puts("");
// for(int i = 2; i <= 2 * m; i++) {
// printf("%d ", change2[i]);
// }
// puts("");
int ans = INF;
for(int i = 2; i <= 2 * m; i++)
ans = min(ans, change1[i] - num[i] + change2[i]);
printf("%d
", ans);
}
return 0;
}