題目描述
堆石子,第 堆有 個石子。將 堆石子合併成三堆,使得這三堆的極差最小。
數據範圍
。
題解
考慮折半搜索,考慮如何將兩邊的三元組合並得到答案。
考慮兩邊的三元組分別爲 和 ,那麼將它們合併後爲 ,爲了方便我們令 ,這樣這兩組的貢獻爲 。
於是我們可以令 ,上述條件就變爲要求 ,求 最小值,所以我們可以按照 排序進行雙指針, 用樹狀數組維護即可。
效率 ,其中 。
代碼
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=6e5+5;
int n,m,a[25],F,G;
LL b[N],s[N],A=2e18;
struct O{LL x,y;}f[N],g[N];
void dfs(int x){
if (x>m){
if (m<n)
f[++F]=(O){s[0]-s[1],s[1]-s[2]};
else g[++G]=(O){s[0]-s[1],s[1]-s[2]};
return;
}
for (int i=0;i<3;i++)
s[i]+=a[x],dfs(x+1),s[i]-=a[x];
}
bool cmp(O x,O y){return x.x<y.x;}
#define lb(z) lower_bound(b+1,b+m+1,z)-b
void upd(int x,LL v){
for (;x;x-=x&-x) s[x]=min(s[x],v);
}
LL qry(int x){
LL v=2e18;
for (;x<=m;x+=x&-x) v=min(v,s[x]);
return v;
}
int main(){
cin>>n;
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
m=n/2;dfs(1);m=n;dfs(n/2+1);
for (int i=1;i<=G;i++) b[i]=g[i].y;
sort(b+1,b+G+1);m=unique(b+1,b+G+1)-b-1;
sort(f+1,f+F+1,cmp);sort(g+1,g+G+1,cmp);
for (int i=1;i<=m;i++) s[i]=2e18;
for (int i=1,j=G;i<=F;i++){
while(j && f[i].x+g[j].x>=0)
upd(lb(g[j].y),g[j].x+g[j].y),j--;
A=min(A,qry(lb(-f[i].y))+f[i].x+f[i].y);
}
cout<<A<<endl;return 0;
}