题目链接

【题目翻译】

给你一个包含n个数字的序列a,你可以将其中某一个数字改成另外一个[1,k]之间的数字。 要求修改之后,对于所有的i∈[1..n/2],a[i]+a[n-i+1]=x,这里的x是一个定值。 n给的一定是偶数。 问你最少操作次数是多少。 一开始给的序列a中的每个数字也都是在[1..k]的范围内。

【题解】

假设我们的目标已经确定了,就是x。那么对于每一对a[i],a[n-i+1]来说。 我们能够把他们的和通过一次操作变到什么程度? 应该是min(a[i],a[n-i+1])+1..max(a[i],a[n-i+1])+k这个范围内。 也就是说我们只需要看一下x这个数字被多少个区间包裹着->对应多少对可以通过一次操作得到x 不过还有不需要操作就直接能变成x的情况,这种情况也会把这个数字包裹住。 所以需要在这个基础上减去恰好为x的情况。 那么需要操作两次的对数有多少呢?当然就是n/2减去1次和0次的情况啦。 x被多少个区间包裹着,可以用左区间+1,右区间减1的方式得到。 这里枚举x要从1..2*k枚举,然后初始化不能用memset,还是得这次用了哪些数字就把哪些数字初始化(1..2*k+1!!!)。 因为$∑k<=2*10^5$所以这样初始化不会超时。

【代码】

#include<bits/stdc++.h>
#define ll long long
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
using namespace std;

const int N = 2e5;

int T;
int n,k;
int a[N+10];
int cnt[N*2+10];
int l[N*2+10];

int main(){
    #ifdef LOCAL_DEFINE
        freopen("D:\\rush.txt","r",stdin);
    #endif
    rei(T);
    while (T--){
        rei(n);rei(k);
        rep1(i,1,n) rei(a[i]);
        rep1(i,1,n/2){
            cnt[a[i]+a[n-i+1]]++;
            int mi = min(a[i],a[n-i+1]),ma = max(a[i],a[n-i+1]);
            mi = mi+1;ma = ma + k;
            l[mi]++;l[ma+1]--;
        }
        int x = 0;
        int ans = -1;
        rep1(i,1,2*k){
            x+=l[i];
            int X = i;
            int tmp = x-cnt[X];
            int tmp2 = n/2-x;
            tmp = tmp + tmp2*2;
            ans = (ans==-1?tmp:min(ans,tmp));
        }
        printf("%d\n",ans);
        rep1(i,1,2*k+1) cnt[i] = 0,l[i] = 0;
    }

    return 0;
}