「杂题乱刷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;
}

浙公网安备 33010602011771号