Codeforces Round 655 (Div. 2) D
阿新 • • 發佈:2020-07-13
-
題意
-
給你一個長度為 n 的陣列(保證 n 為奇數),陣列頭尾相連變成一個環。現在你需要對這個環進行多次如下操作直到陣列中只剩下一個數,使得最後這個數最大。
-
操作方法:選擇陣列中的一個數,將其替換為相鄰左右兩數的合,並且刪除掉左右兩邊的數。
也就是說跟原陣列相比,x 位置上的數變成 \(x-1\) 和 \(x+1\) 位置上的數的和(這裡要注意陣列首尾相連是一個環狀的),而 \(x-1\) 和 \(x+1\) 位置上的數則刪去,每次操作刪除兩個數,因為保證 n 為奇數,所以總共的操作共 \((n-1)/2\) 次。
-
-
解析
- 剛開始很容易想到,最後得到的這個數,是由陣列中的 \((n-1)/2 + 1\)
- 那我們就要另闢蹊徑,選擇找出被替換的數,我們知道運算元為 \((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])\)。
- 剛開始很容易想到,最後得到的這個數,是由陣列中的 \((n-1)/2 + 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;
}