#4789. 啊孤独感放辣椒

题目描述

$n$ 堆石子,第 $i$ 堆有 $a_i$ 个石子。将 $n$ 堆石子合并成三堆,使得这三堆的极差最小。

数据范围

$n le 24,a_i le 10^9$ 。

题解

考虑折半搜索,考虑如何将两边的三元组合并得到答案。

考虑两边的三元组分别为 $(a_1,b_1,c_1)$ 和 $(a_2,b_2,c_2)$ ,那么将它们合并后为 $(a_1+a_2,b_1+b_2,c_1+c_2)$ ,为了方便我们令 $a_1+a_2 ge b_1+b_2 ge c_1+c_2$ ,这样这两组的贡献为 $a_1+a_2-c_1-c_2$ 。

于是我们可以令 $f=a-b,g=b-c$ ,上述条件就变为要求 $f_1+f_2,g_1+g_2 ge 0$ ,求 $f_1+f_2+g_1+g_2$ 最小值,所以我们可以按照 $f$ 排序进行双指针, $g$ 用树状数组维护即可。

效率 $O(NlogN)$ ,其中 $N=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;
}
原文地址:https://www.cnblogs.com/xjqxjq/p/12727363.html