#4789. 啊孤獨感放辣椒

題目描述

nn 堆石子,第 ii 堆有 aia_i 個石子。將 nn 堆石子合併成三堆,使得這三堆的極差最小。

數據範圍

n24,ai109n \le 24,a_i \le 10^9

題解

考慮折半搜索,考慮如何將兩邊的三元組合並得到答案。

考慮兩邊的三元組分別爲 (a1,b1,c1)(a_1,b_1,c_1)(a2,b2,c2)(a_2,b_2,c_2) ,那麼將它們合併後爲 (a1+a2,b1+b2,c1+c2)(a_1+a_2,b_1+b_2,c_1+c_2) ,爲了方便我們令 a1+a2b1+b2c1+c2a_1+a_2 \ge b_1+b_2 \ge c_1+c_2 ,這樣這兩組的貢獻爲 a1+a2c1c2a_1+a_2-c_1-c_2

於是我們可以令 f=ab,g=bcf=a-b,g=b-c ,上述條件就變爲要求 f1+f2,g1+g20f_1+f_2,g_1+g_2 \ge 0 ,求 f1+f2+g1+g2f_1+f_2+g_1+g_2 最小值,所以我們可以按照 ff 排序進行雙指針, gg 用樹狀數組維護即可。

效率 O(NlogN)O(NlogN) ,其中 N=3n2N=3^{\frac{n}{2}}

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章