peiwenjun's blog 没有知识的荒原

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\) ,则:

\[f_k=\big(\sum_{i=k+1}^n\frac{a_i}{2^{n-i+1}}+\frac{a_{k+1}}{2^{n-k}}\big)-\big(\sum_{i=1}^k\frac{a_i}{2^i}+\frac{a_k}{2^k}\big)\\ \]

答案为 \(\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}\) ,则:

\[\Delta_k=\frac{d_{k+1}}{2^{n-k}}-\frac{d_k}{2^{k-1}}\\ \]

进一步,对于 \(p\lt q\) ,有:

\[\begin{aligned} f_q-f_p&=\sum_{k=p+1}^q\Delta_k\\ &=\frac{d_{q+1}}{2^{n-q}}+\sum_{k=p+2}^qd_k(\frac 1{2^{n-k+1}}-\frac 1{2^{k-1}})-\frac{d_{p+1}}{2^p}\\ \end{aligned} \]

小技巧:遇到 \(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;
}

posted on 2023-07-04 23:58  peiwenjun  阅读(9)  评论(0)    收藏  举报

导航