Codeforces 1343D - Constant Palindrome Sum (差分數組)



題面

Prob




題意

給定一個長度為 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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM