天天看點

Codeforces Round #636 (Div. 3) ——D. Constant Palindrome Sum 題解

題目連結:https://codeforces.com/contest/1343/problem/D

Codeforces Round #636 (Div. 3) ——D. Constant Palindrome Sum 題解
Codeforces Round #636 (Div. 3) ——D. Constant Palindrome Sum 題解

題意是說改變給定數組中某些元素,使得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;
}
           
cf