CF1817E Half-sum 题解
题目描述
\(T\) 组数据,给定一个非负整数序列 \(\{a_1,\cdots,a_n\}\) 。
每次你可以取两个数删除,然后加入它们的平均数。
求剩下两个数差的绝对值的最大值,对 \(10^9+7\) 取模。
数据范围
- \(1\le T\le 100,2\le\sum n\le 10^6,0\le a_i\le 10^9\) 。
时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{256MB}\) 。
分析
将所有数升序排序,显然对前缀从前往后合并,对后缀从后往前合并最优。
具体的,假如分界点为 \(k\) ,则:
答案为 \(\min\limits_{1\le k\lt n}f_k\) ,这是一个 \(\mathcal O(n^2)\) 的算法。
对 \(f\) 做差分。记 \(d_i=a_i-a_{i-1},\Delta_i=f_i-f_{i-1}\) ,则:
进一步,对于 \(p\lt q\) ,有:
小技巧:遇到 \(2\) 的幂次比较大小通常可以观察性质将枚举量压缩在 \(\log\) 以内。
如果 \(q\le\frac n2\) ,则 \(\frac 1{2^{n-k+1}}-\frac 1{2^{k-1}}\le 0\) , \(f_q\) 比 \(f_p\) 优的必要条件为 \(\frac{d_{q+1}}{2^{n-q}}\ge\frac{d_{p+1}}{2^p}\) 。
注意到 \(d_i\in[0,10^9]\) ,如果 \(d_{p+1}\gt 0\and n-q\ge p+30\) ,则上式不可能成立。
因此对于 \(p\in[1,\frac n2-30]\) ,我们只需要考虑第一个 \(d_{p+1}\neq 0\) 的 \(p\) ,及其后面 \(30\) 个位置即可,如果这样的 \(p\) 不存在再考虑 \([\frac n2-30,\frac n2]\) 。
一种等价的实现方式是考虑前 \(30\) 个 \(d_{p+1}\neq 0\) 的 \(p\) ,同理对于 \(p\ge 30\) 的部分,我们只需要考虑后 \(30\) 个 \(d_{q+1}\neq 0\) 的 \(q\) 。
将每次计算结果用 \(\frac 12\) 进制表示并比较大小,输出答案时再考虑取模的问题即可。
时间复杂度 \(\mathcal O(n\log v)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inv2=(mod+1)/2;
int n,t;
int a[maxn],p[maxn],q[maxn];
void check(int k)
{
for(int i=0;i<=n;i++) p[i]=0;
for(int i=1;i<=n;i++)
if(i<=k) p[min(i,k-1)]-=a[i];
else p[min(n-i+1,n-k-1)]+=a[i];
for(int i=n;i>=1;i--)
{
int x=p[i]>>1;
p[i-1]+=x,p[i]-=2*x;
}
int flg=0;
for(int i=0;i<=n;i++)
if(p[i]!=q[i])
{
flg=p[i]>q[i];
break;
}
if(flg) for(int i=0;i<=n;i++) q[i]=p[i];
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<=n;i++) q[i]=0;
sort(a+1,a+n+1);
for(int i=1,cnt=30;i<=n-1&&cnt;i++) if(a[i]!=a[i+1]) check(i),cnt--;
for(int i=n-1,cnt=30;i>=1&&cnt;i--) if(a[i]!=a[i+1]) check(i),cnt--;
int res=0;
for(int i=n;i>=0;i--) res=(1ll*res*inv2+q[i])%mod;
printf("%d\n",res);
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17527430.html
浙公网安备 33010602011771号