「杂题乱刷2」AT_abc348_g

题目链接

AT_abc348_g [ABC348G] Max (Sum - Max)

解题思路

考虑对于一个 \(k\) 我们怎么做。

注意到我们可以先把 max 这一维给从小到大排序,然后依次枚举这个 \(b_i\) 并顷定 \(b_i\) 为你选取所有 \(b_i\) 中的最大值,考虑此时如何最优,显然选取 \(k\) 个下标 \(j_1, j_2, j_3, \dots, j_k\) 满足 \(l \in [1,k], \ j_l \in [1,i]\)\(j_l\) 互不相同,\(\sum_{i=1}^{k} a_{j_i}\) 的值最大,发现这东西就是 \(a\) 序列前 \(i\) 个数字中的前 \(k\) 大值,可持久化线段树维护即可。

接下来我们考虑将 \(k\) 从小到大依次做,我们发现给予我们的决策是单调不降的,具体的,若 \(j - 1\) 这个决策劣于 \(j\) 这个决策,则 \(j\) 这个决策将会永远优于 \(j - 1\) 这个决策,于是加上决策单调性优化即可,使用分治来维护,时间复杂度 \(O(n \log n \log V)\)

参考代码

ll n;
struct node{
    ll x,y;
}a[200010];
bool cmp(node x,node y){
    return x.y<y.y;
}
ll rt[200010*32],id;
ll ls[200010*32],rs[200010*32],k;
ll val[200010*32],S[200010*32];
void pushup(ll x){
    val[x]=val[ls[x]]+val[rs[x]],
    S[x]=S[ls[x]]+S[rs[x]];
}
void upd(ll pre,ll&x,ll l,ll r,ll y)
{
    if(!x)
        x=++id;
    if(l==r)
    {
        val[x]=val[pre]+y;
        S[x]=S[pre]+1;
        return ;
    }
    if(y<=mid)
        rs[x]=rs[pre],
        upd(ls[pre],ls[x],l,mid,y);
    else
        ls[x]=ls[pre],
        upd(rs[pre],rs[x],mid+1,r,y);
    pushup(x);
}
ll query(ll x,ll l,ll r,ll k)
{
    if(!k)
        return 0;
    if(l==r)
        return k*l;
    if(S[rs[x]]>=k)
        return query(rs[x],mid+1,r,k);
    return val[rs[x]]+query(ls[x],l,mid,k-S[rs[x]]);
}
ll ans[200010];
void sol(ll l,ll r,ll L,ll R) //[l,r] 决策点为 [L,R]
{
    if(l>r)
        return ;
    ll id=-1,l2=max(L,mid);
    ans[mid]=-9e18;
    forl(i,l2,R)
    {
        ll now=query(rt[i],-1e9,1e9,mid);
        if(now-a[i].y>ans[mid])
            ans[mid]=now-a[i].y,
            id=i;
    }
    sol(l,mid-1,L,id);
    sol(mid+1,r,id,R);
}
void _clear(){}
void solve()
{
    _clear();
    cin>>n;
    forl(i,1,n)
        cin>>a[i].x>>a[i].y;
    sort(a+1,a+1+n,cmp);
    forl(i,1,n)
        upd(rt[i-1],rt[i],-1e9,1e9,a[i].x);
    sol(1,n,1,n);
    forl(i,1,n)
        cout<<ans[i]<<endl;
}
posted @ 2025-04-09 15:17  wangmarui  阅读(18)  评论(0)    收藏  举报