1. 程式人生 > 實用技巧 >Codeforces Round 655 (Div. 2) D

Codeforces Round 655 (Div. 2) D

  • 題意

    • 給你一個長度為 n 的陣列(保證 n 為奇數),陣列頭尾相連變成一個環。現在你需要對這個環進行多次如下操作直到陣列中只剩下一個數,使得最後這個數最大。

    • 操作方法:選擇陣列中的一個數,將其替換為相鄰左右兩數的合,並且刪除掉左右兩邊的數。
      也就是說跟原陣列相比,x 位置上的數變成 \(x-1\)\(x+1\) 位置上的數的和(這裡要注意陣列首尾相連是一個環狀的),而 \(x-1\)\(x+1\) 位置上的數則刪去,每次操作刪除兩個數,因為保證 n 為奇數,所以總共的操作共 \((n-1)/2\) 次。

  • 解析

    • 剛開始很容易想到,最後得到的這個數,是由陣列中的 \((n-1)/2 + 1\)
      (令為 m )個數的和組成的,但又不是任意 m 個數所組合出的數一定能行,在自己的思考過程中會發現,如果考慮對 n 個數中進行題目要求的操作方法,得到的和是由 m 個數組成,這 m 個數有的相鄰而有的不相鄰,不是很好考慮。
    • 那我們就要另闢蹊徑,選擇找出被替換的數,我們知道運算元為 \((n-1)/2\) (令為 k ) 次,所以就替換了 k 個數,也就是說這 k 個數沒有對答案有貢獻,所以答案就是 \(Sum - k個數的和\)。因為在操作的時候,會刪除掉被替換位置兩邊的數,所以被替換的數是一定沒有相鄰的。那麼我們只要找到所有可能的 k 個數的和,選擇其最小的即可得到答案。
    • 那麼如何找到所有的 k 個數的和呢?實際上就是 1~n 從其中每一個位置為起點,隔一個數取一個數,共取 \((n-1)/2\)
      次。
    • 例如:當 n = 5 時,有 5 種取法,分別是 (1,3),(2,4),(3,5),(4,1),(5,2)。
    • 如果 n = 5 還不能理解,試著想想 n = 7 的情況,如果剛開始取走了 1 和 3,那麼我下一步可以取走 5 或 6,那可能會問,取走 6 這時不就不是隔一個數取一個數,這時如果理解為從 6 開始,那就是 (6,1,3),依舊是隔一個數取一個數
    • 現在我們已經找到了所有可能的 k 個數的構成,那麼要如何高效的得到和呢,現在我們將原陣列倍長,並預處理好奇數位置上和偶數位置上的字首和,之後就是計算 \(Min(sum[i+n-2]-sum[i-1])\)

  • 最後上程式碼

#include<bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
const int Maxn = 2e5+10;
const int Inf = 0x7f7f7f7f;
 
int a[Maxn<<1];
ll Sum[Maxn<<1],tol;
 
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),a[i+n] = a[i],tol += a[i];
    Sum[1] = a[1];
    for(int i=2;i<=2*n;i++)
        Sum[i] = Sum[i-2] + a[i];
    ll Min = 1ll*Inf*Inf;
    for(int i=1;i<=n;i++)
    {
        ll tmp = Sum[i+n-2] - Sum[i-1];
        Min = min(Min,tmp);
    }
    printf("%lld\n",tol-Min);
    return 0;
}