题目大意:
给出长度为(n)的数列(a_i)和(k),保证(a_i leq k)。将(a)中任意一个数(a_i)改成([1, k])中的一个数称为一次操作。问最少经过多少次操作后,(x = a_i+a_{n-i+1} (i leq frac{n}{2}))全部相等。
思路:
首先考虑暴力枚举(x),容易得到一个(O(nk))的算法,时间瓶颈在对(x)的枚举之上,我们考虑怎么优化。
手玩一下即可发现,对于每一对((i, n - i + 1)),至多进行两次修改。我们假设(x)的操作次数为(cnt[x]),对于每一对((i, n - i + 1)),具体的:
- 如果(a[i] + a[n - i + 1] == x),那么(cnt[x] += 0)
- 如果(max(a[i], a[n - i + 1]) + 1 leq x) && (min(a[i], a[n - i + 1]) + k geq x),除掉等于(x)的情况(cnt[x] += 1)
- ELSE, (cnt[x] += 2)
不难发现,对于操作次数为(1)的pair是连续的一段区间,可以想到使用差分(O(1))的修改这段区间(x)的操作次数,最后(O(k))的更新答案。
具体实现上:
(cnt1[x])表示选用(x)作为每对pair的结果时,修改一次的pair数量。
差分的每次修改都相当于对([L:])区间进行修改。
Code:
ll n, k, a[N], cnt0[N], cnt1[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) {
//init
ll ans = INF;
cin >> n >> k;
for (int i = 1; i <= (k << 1); i++)
cnt0[i] = cnt1[i] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n / 2; i++) {
cnt0[a[i] + a[n - i + 1]]++;
cnt1[min(a[i], a[n - i + 1]) + 1]++, cnt1[max(a[i], a[n - i + 1]) + k + 1]--;
cnt1[a[i] + a[n - i + 1]]--, cnt1[a[i] + a[n - i + 1] + 1]++; //去掉修改次数为0
}
for (int i = 2; i <= (k << 1); i++) { //枚举答案
cnt1[i] += cnt1[i - 1];
ckmin(ans, cnt1[i] + 2ll * (n / 2 - cnt0[i] - cnt1[i]));
}
cout << ans << "
";
}
return 0;
}