【学习笔记】CF1817E Half-sum
前人之述备矣。
首先最优的贪心策略为:将原数组 排序 后,选择一个分界点$p$,然后对于$[1,p]$从后往前合并,$[p+1,n]$从前往后合并,将前后两个数的差值作为答案。模拟二进制下的加法即可做到$O(n)$检查。注意这里不需要均摊维护高精度加法(这样做会多一个$\log$),因为我们不关心中途的结果。
然后,从一次复合函数的角度推导(这样形式更为简洁):
设$f(a_1,...,a_i,x)=ax+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$
注意到$a=\frac{1}{2^{i}}$,因此从增长率的角度来看,应该只选前$30$项和后$30$项进行检查。
通常,我们可以通过邻项比较(找 变化量)的方法来排除掉一些一定不优的方案。
发现$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)$,因此考虑原数组的差分数组$d_i=a_{i+1}-a_i$,那么从方案$i$到$j$的变化量为: 注意到,如果$d_i=0$那么$i$一定不会成为最优分界点,因此我们 应该满足 $d_i>0$ 。那么,当$i\le j\le \frac{n}{2}$时,中间的式子一定$\le 0$,只有当$n-j\le i+30$时才可能成为答案。即:$j\ge n-i-30$。这样,我们只要找到第一个满足$\le \frac{n}{2}-30$并且$b_i>0$的位置,这一定是最优的;如果不存在,那么检验$[\frac{n}{2}-30,\frac{n}{2}]$中的所有位置。其等价实现是,检验前$30$个满足$b_i>0$的位置。对于$j>\frac{n}{2}$的情况显然是同理的。 复杂度$O(n\log V)$。
$$-\frac{d_i}{2^i}-\sum_{i1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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";
}
}
【学习笔记】CF1817E Half-sum