Constant Palindrome Sum
题意
给定一个长度为 n n n(保证 n n n是偶数)的数字序列 a n a_n an和一个 k k k,可以对序列进行若干次操作,每次操作可以将其中一个 a i a_i ai替换成 [ 1 , k ] [1,k] [1,k]中的任意一个数,要求经过若干次操作后对于每个 i i i, i ≤ n 2 i\leq\frac{n}{2} i≤2n, 都满足 a i + a n − 1 + i = s u m a_{i}+a_{n-1+i}=sum ai+an−1+i=sum,要求最少的操作次数。
思路
很明显,
s
u
m
sum
sum的最终取值只能是
[
2
,
2
∗
k
]
[2,2*k]
[2,2∗k]。
我们可以枚举
s
u
m
sum
sum,对每一个
s
u
m
sum
sum计算最少操作次数,然后对所有的最少操作次数取最小值,就可得到最终答案。
具体来说,要满足条件,对于每一对
(
a
i
,
a
n
−
1
+
i
)
(a_{i},a_{n-1+i})
(ai,an−1+i),我们有三种操作:
- 这一对之和已经等于 s u m sum sum,不需要操作。使用 c n t [ a [ i ] + a [ n − 1 + i ] ] cnt[a[i]+a[n-1+i]] cnt[a[i]+a[n−1+i]]记录这个和出现的次数。
- 这一对之和不等于 s u m sum sum,可以只经过一次操作到之和等于 x x x。对于每一对来说,它们经过一次操作所得的两个数之和是有上下限的。我们可以记录这个上下限出现的次数,利用前缀和来统计有几对只经过一次操作就可满足条件。设下限为 x x x,上限为 y y y,然后令 p r e f [ x ] + + , p r e f [ y + 1 ] − − pref[x]++,pref[y+1]-- pref[x]++,pref[y+1]−−,再对 p r e f pref pref求前缀和。这样, p r e f [ s u m ] pref[sum] pref[sum]便表示只经过一次操作就能满足条件的数对的个数,这些数对中也包括哪些不需要操作的数对,所以最终必须经过一次操作的数对个数为 p r e f [ s u m ] − c n t [ s u m ] pref[sum]-cnt[sum] pref[sum]−cnt[sum]。
- 这一对之和不等于 x x x,必须经过两次操作到之和等于 x x x。由2知,总数对个数 n 2 \frac{n}{2} 2n减去 p r e f [ s u m ] pref[sum] pref[sum]就是需要经过两次操作才能满足条件的数对个数。
代码
#include<bits/stdc++.h>
using namespace std;
int t;
int n,k;
int a[200005];
int main()
{
cin >> t;
while(t--){
cin >> n >> k;
vector<int> cnt(2*k+1);
vector<int> pref(2*k+2);
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int i = 1; i <= n/2; ++i){
++cnt[a[i]+a[n-i+1]];
int x = min(a[i],a[n-i+1])+1;//下限
int y = max(a[i],a[n-i+1])+k;//上限
++pref[x];
--pref[y+1];
}
for(int i = 1; i <= 2*k+1; ++i)
pref[i] += pref[i-1];
int ans = 1e9;
for(int sum = 2; sum <= 2*k; ++sum)//枚举sum
ans = min(ans,(pref[sum]-cnt[sum])+(n/2-pref[sum])*2);
printf("%d\n",ans);
}
return 0;
}