題面
題意
給定一個長度為 n 的數列,n 為偶數,保證每個元素在 [ 1 , k ] 之間
每次操作可以把某個位置的數字變成 [ 1 , k ] 內的任意數字
要求讓這個數列滿足:對於所有的 i ∈ [ 1 , n/2 ],a[i] + a[n-i+1] 是一個定值
問最少的操作次數
解題思路
差分數組維護取某個值為定值時所需要的最少操作次數
令差分數組為 delta
每次取一組 a[i] 和 a[n-i+1] 處理
令 sum = a[i]+a[n-i+1] , maxn=max(a[i],a[n-i+1]) , minn=min(a[i],a[n-i+1])
分類討論得到:
一、如果定值 x 在 [2,minn] 之間,即使將較大的數更改為1后,和也是大於x,說明此時這兩個數都需要更改,所以這段區間的操作數+2
二、如果定值 x 在 [maxn+k+1,2*k] 之間,即使將較小的數更改為k后,和也是小於x,說明此時這兩個數都需要更改,所以這段區間的操作數+2
三、如果定值 x 在 [minn+1,maxn+k] 之間且不等於 sum,能夠做到只改變其中一個數就使得和等於x,所以這個范圍內操作數+1
四、特殊處理,如果定值 x 等於 sum,不需要更改任何一個數,所以這個點的操作數不需要增加
綜上,對於差分數組的標記,我們可以得到:
delta[2]+=2;
delta[minn+1]-=2;
//討論 1
delta[maxn+k+1]+=2;
delta[2*k+1]-=2;
//討論 2
delta[minn+1]++;
delta[maxn+k+1]--;
delta[sum]--;
delta[sum+1]++;//因為上面兩步把sum位置加上了1,所以這里減去1
//討論 3、4
差分數組的標記原理為:如果要讓區間 [l,r] 內每個元素加上 x ,則 delta[l]+=x , delta[r+1]-=x ,最后遍歷從前往后依次加上前一個數作為答案
對於代碼中“討論 3、4”為什么sum先加后減的解釋
如果要求的和恰好是當前處理的兩數之和 sum
那么 sum 這個點的操作次數不需要增加
又因為 sum∈[ minn+1 , maxn+k ]
所以理解方法可以是以下這兩種——
- 先在整段區間上 +1 ,再將sum這個點單獨 -1
整段區間 +1 ,即delta[minn+1]++; delta[maxn+k+1]--;
單獨將 sum 這個點 -1 ,即加上負 1 ,即delta[sum]--; delta[sum+1]++;
- 將區間看成兩段,分別是 [ minn+1 , sum-1 ] 和 [ sum+1 , maxn+k ] ,然后兩段分別 +1
前一段即delta[minn+1]++; delta[sum]--;
后一段即delta[sum+1]++; delta[maxn+k+1]--;
兩種理解的代碼合起來是相同的
對於 2*k+1 這個位置,是差分數組標記的結尾,可以選擇不進行處理
所以整理得到:
delta[2]+=2;
delta[minn+1]--;
delta[maxn+k+1]++;
delta[sum]--;
delta[sum+1]++;
最后從 2 位置開始到 2*k 處理一遍差分數組即可
ans=delta[2];
for(int i=3;i<=2*k;i++)
{
delta[i]+=delta[i-1];
ans=min(delta[i],ans);
}
完整程序
(62ms/1000ms)
在 i ∈ [1,n/2] 時,n-i+1會返回數列的后半部分與 i 對應的位置
在 i ∈ [n/2+1,n] 時,n-i+1會返回數列的前半部分與 i 對應的位置
所以在 i>n/2 時使用 ar[i] 與 ar[n-i+1] 也是對的
#include<bits/stdc++.h>
using namespace std;
int ar[200050],delta[400050]; //差分數組開2*k的空間
void solve()
{
int n,k,sum,minn,maxn,ans;
cin>>n>>k;
memset(delta,0,(2*k+10)*sizeof(int)); //初始化到2*k即可(稍大)
for(int i=1;i<=n;i++)
{
cin>>ar[i];
if(i>n/2)
{
sum=ar[i]+ar[n-i+1];
minn=min(ar[i],ar[n-i+1]);
maxn=sum-minn;
delta[2]+=2;
delta[minn+1]--;
delta[maxn+k+1]++;
delta[sum]--;
delta[sum+1]++;
}
}
ans=delta[2];
for(int i=3;i<=2*k;i++)
{
delta[i]+=delta[i-1];
ans=min(delta[i],ans);
}
cout<<ans<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;
for(int t=1;t<=T;t++)
solve();
return 0;
}