NOIP2023模拟9联测30 T3 高爸

发布时间 2023-11-02 22:28:46作者: 彬彬冰激凌

NOIP2023模拟9联测30 T3 高爸

三分啊,三分……

思路

设现在的平均力量值为 \(x\),大于 \(x\) 力量值的龙有 \(n\) 条,小于等于的龙有 \(m\) 条,花费为:

\[a(n \times x-\sum_{i=1}^{n+m} p_i (p_i>x))+b(\sum_{i=1}^{n+m} p_i (p_i \leq x) - m \times x) \]

对于 \(a(n \times x-\sum_{i=1}^{n+m} p_i (p_i>x))\)\(b(\sum_{i=1}^{n+m} p_i (p_i \leq x) - m \times x)\) 来说,都具有三分性质,凹函数的和也具有三分性质,所以可以三分。

这里写的是关于 \(x\) 值域的三分。

\(p_i\) 离散化后,钦定 \(x\) 后求答案时,使用树状数组求 \(p_i\) 的前缀和和小于 \(x\) 的个数。

三分时间复杂度 \(\log n\),树状数组时间复杂度 \(\log n\),总复杂度 \(O(n \log^2 n)\)

CODE

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const ll maxn=1e5+5;

int n,cnt;
int nx[maxn],tmp[maxn],fp[maxn],x[maxn];

ll a,b,sum;
ll ts[maxn],tc[maxn];

unordered_map<ll,int>mp;

set< ll >s;

inline ll read() {
    ll s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
inline void write(ll X)
{
    if(X<0) {X=~(X-1); putchar('-');}
    if(X>9) write(X/10);
    putchar(X%10+'0');
}

inline void lsh()
{
    copy(x+1,x+n+1,tmp+1);
    sort(tmp+1,tmp+n+1);
    tmp[0]=-1;
    for(ll i=1;i<=n;i++)
        if(tmp[i]!=tmp[i-1]) mp[tmp[i]]=++cnt,fp[cnt]=tmp[i];
    for(ll i=1;i<=n;i++) nx[i]=mp[x[i]];
}

ll lowbit(ll x){return x&(-x);}
inline void updata(int x,ll y)
{
    while(x<=n)
    {
        ts[x]+=y;
        tc[x]++;
        x+=lowbit(x);
    }
}
inline pair<ll,ll> gtsum(int x)
{
    ll sumcnt=0,sumsum=0;
    while(x)
    {
        sumsum+=ts[x];
        sumcnt+=tc[x];
        x-=lowbit(x);
    }
    return make_pair(sumcnt,sumsum);
}

inline ll gt(ll x,ll t)
{
    auto k=s.upper_bound(x);
    if(k==s.begin()) return (sum-t*x)*b;
    k--;
    ll tp=0;
    tp=mp[*k];
    pair<ll,ll> tmp=gtsum(tp);
    ll tcnt=tmp.first,tsum=tmp.second;
    return (tcnt*x- tsum)*a+(sum-tsum-(t-tcnt)*x)*b;
}
inline ll gtans(int t)
{
    int l=1,r=1e9;
    ll ans=2e18;
    while(l<=r)
    {
        int mid=l+r>>1;
        int midl=mid+l>>1,midr=mid+r>>1;
        ll vm=gt(mid,t),vml=gt(midl,t),vmr=gt(midr,t);
        if(vml<=vm&&vm<=vmr) ans=vml,r=midr-1;
        else if(vml>=vm&&vm>=vmr) ans=vmr,l=midl+1;
        else ans=vm,l=midl+1,r=midr+1;
    }
    return ans;
}

signed main()
{
    scanf("%d%lld%lld",&n,&a,&b);
    for(int i=1;i<=n;i++) x[i]=read();

    lsh();

    printf("0\n");
    updata(nx[1],x[1]);
    sum=x[1];
    s.insert(x[1]);
    for(int i=2;i<=n;i++)
    {
        sum+=x[i];
        updata(nx[i],x[i]);
        s.insert(x[i]);
        ll ans=gtans(i);
        write(ans);
        putchar('\n');
    }
}

后记

常数有点小大,需要快读快输和 inline。