首先最优的贪心策略为:将原数组 排序 后,选择一个分界点 p p p,然后对于 [ 1 , p ] [1,p] [1,p]从后往前合并, [ p + 1 , n ] [p+1,n] [p+1,n]从前往后合并,将前后两个数的差值作为答案。模拟二进制下的加法即可做到 O ( n ) O(n) O(n)检查。注意这里不需要均摊维护高精度加法(这样做会多一个 log \log log),因为我们不关心中途的结果。
然后,从一次复合函数的角度推导(这样形式更为简洁):
设 f ( a 1 , . . . , a i , x ) = a x + b f(a_1,...,a_i,x)=ax+b f(a1,...,ai,x)=ax+b,那么 f ( a 1 , . . . , a i , a i + 1 , x ) = a ⋅ a i + 1 + x 2 + b = 1 2 a x + 1 2 a ⋅ a i + 1 + b f(a_1,...,a_i,a_{i+1},x)=a\cdot \frac{a_{i+1}+x}{2}+b=\frac{1}{2}ax+\frac{1}{2}a\cdot a_{i+1}+b f(a1,...,ai,ai+1,x)=a⋅2ai+1+x+b=21ax+21a⋅ai+1+b
注意到 a = 1 2 i a=\frac{1}{2^{i}} a=2i1,因此从增长率的角度来看,应该只选前 30 30 30项和后 30 30 30项进行检查。
通常,我们可以通过邻项比较(找 变化量)的方法来排除掉一些一定不优的方案。
发现 f ( a 1 , . . . , a i + 1 ) − f ( a 1 , . . . , a i ) = 1 2 a ⋅ ( a i + 1 − a i ) = 1 2 i ( a i + 1 − a i ) f(a_1,...,a_{i+1})-f(a_1,...,a_i)=\frac{1}{2}a\cdot(a_{i+1}-a_i)=\frac{1}{2^i}(a_{i+1}-a_i) f(a1,...,ai+1)−f(a1,...,ai)=21a⋅(ai+1−ai)=2i1(ai+1−ai),因此考虑原数组的差分数组 d i = a i + 1 − a i d_i=a_{i+1}-a_i di=ai+1−ai,那么从方案 i i i到 j j j的变化量为: − d i 2 i − ∑ i < k < j d k ( 1 2 k − 1 2 n − k ) + d j 2 n − j -\frac{d_i}{2^i}-\sum_{i
注意到,如果 d i = 0 d_i=0 di=0那么 i i i一定不会成为最优分界点,因此我们 应该满足 d i > 0 d_i>0 di>0 。那么,当 i ≤ j ≤ n 2 i\le j\le \frac{n}{2} i≤j≤2n时,中间的式子一定 ≤ 0 \le 0 ≤0,只有当 n − j ≤ i + 30 n-j\le i+30 n−j≤i+30时才可能成为答案。即: j ≥ n − i − 30 j\ge n-i-30 j≥n−i−30。这样,我们只要找到第一个满足 ≤ n 2 − 30 \le \frac{n}{2}-30 ≤2n−30并且 b i > 0 b_i>0 bi>0的位置,这一定是最优的;如果不存在,那么检验 [ n 2 − 30 , n 2 ] [\frac{n}{2}-30,\frac{n}{2}] [2n−30,2n]中的所有位置。其等价实现是,检验前 30 30 30个满足 b i > 0 b_i>0 bi>0的位置。对于 j > n 2 j>\frac{n}{2} j>2n的情况显然是同理的。
复杂度 O ( n log V ) O(n\log V) O(nlogV)。
#include
#define ll long long
#define pb push_back
#define fi first
#define se second
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=1e9+7;
const int N=1e6+65;
int T,n,m,res;
ll a[N],inv2=(mod+1)/2;
ll b[N],c[N],r[N];
void solve(int x){
for(int i=0;i<=n+60;i++)b[i]=c[i]=0;
for(int i=x;i>=1;i--){
int j=i-(i==x);
b[n-j]+=a[i];
}for(int i=0;i<=n+60;i++)b[i+1]+=b[i]/2,b[i]%=2;
for(int i=x+1;i<=n;i++){
int j=n-i+1-(i==x+1);
c[n-j]+=a[i];
}for(int i=0;i<=n+60;i++)c[i+1]+=c[i]/2,c[i]%=2;
for(int i=0;i<=n+60;i++){
c[i]-=b[i];if(c[i]<0)c[i]+=2,c[i+1]--;
}
int it=n+60;while(it&&c[it]==r[it])it--;
if(c[it]>r[it]){
for(int i=0;i<=n+60;i++)r[i]=c[i];
res=x;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--){
cin>>n;for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);res=1;for(int i=0;i<=n+60;i++)r[i]=0;
int j=1;
for(int i=1;i<=30;i++){
while(j<n&&a[j]==a[j+1])j++;
if(j==n)break;solve(j),j++;
}j=n;
for(int i=1;i<=30;i++){
while(j>1&&a[j]==a[j-1])j--;
if(j==1)break;solve(j-1),j--;
}ll x=a[res];
for(int i=res-1;i>=1;i--)x=(x+a[i])*inv2%mod;
ll y=a[res+1];
for(int i=res+2;i<=n;i++)y=(y+a[i])*inv2%mod;
cout<<(y-x+mod)%mod<<"\n";
}
}