題目連結:https://codeforces.com/contest/1343/problem/D
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TP35UNNpmT10kaNBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4YDN1EzM1IjMyIDNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
題意是說改變給定數組中某些元素,使得a[i]與a[n - i - 1]的和為常數。
周遊每一對,假設這一輪讀到的數是 x 和 y(x < y),那麼改變其中一個,最大和最小的和是多少呢?
将較小的 x 變成 k,得到最大和 y + k
将較大的 y 變成 1,得到最小和 x + 1
是以對于某一對 (x, y),改變1個數情況下和的取值範圍為(x + 1, y + k),改變2個數就能取到任意(2, 2k)中間的值。
但是周遊每一對的複雜度為 n / 2, 如果每次都記錄整個區間的操作次數,時間複雜度要乘上 k, 就TLE了。是以今天學了新的差分數組,相當于字首和的逆過程:隻對某個區間的頭和尾進行記錄,最後整個區間計算字首和即可。
例如,k = 3, 目前和為4,改變一個數的取值範圍為(3, 5),那麼根據前面的分析,如果最終選擇的定值x在(3,5)區間内,隻需要改變1次(或0次,對應4),而在區間外則需要改變2次。那麼我們對差分數組做如下處理:
(1)diff[2] += 2, diff[2 * k] -= 2; 先初始化整個區間都需要2次
(2)diff[l] -= 1, diff[r + 1] += 1; 将(l, r)區間内的次數減1
(3)diff[sum] -= 1, diff[sum + 1] += 1;将sum對應的次數再減1變為0
(4)最後從頭到尾,diff[i] += diff[i - 1],就将整個區間覆寫了(這裡可以手寫試一試)
代碼如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[200010];
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, k;
scanf("%d %d", &n, &k);
for(int i = 0; i < n; i++) {
cin >> a[i];
}
int res[2 * k + 1] = {0};
int sum;
for(int i = 0, j = n - 1; i < j; i++, j--) {
sum = a[i] + a[j];
int l = sum - max(a[i], a[j]) + 1;
int r = sum - min(a[i], a[j]) + k;
res[2] += 2;
res[l] -= 1;
res[sum] -= 1;
res[sum + 1] += 1;
res[r + 1] += 1;
}
int ans = res[2];
for(int i = 3; i <= 2 * k; i++) {
res[i] += res[i - 1];
ans = min(res[i], ans);
}
cout << ans << endl;
}
return 0;
}