来补题辣~

这题很简单,我们来判断下一个选择的序列是否合法的充要条件是什么:

合法是要满足对于所有的选择进去的i,都要满足s[i]>=总人数

等价于min(s[i])>=总人数

所以,我们可以直接枚举这个min(s[i]),然后问题就转化成了,在一定范围内,选s[i]-1个人的最大战力,那么我们选择前s[i]-1个人即可(需要注意的是,可能没有s[i]-1个人,这个时候范围内全选即可)

所以,做法就是,我们先把所有士兵按s[i]从小到大排序,然后统计出i+1-n中,前s[i]-1大的数的和是多少,注意到,如果我们倒着枚举的话,可以减少删除的操作,所以我们倒着枚举i,然后,用一颗权值线段树维护计算即可

具体是,对每个节点我们维护当前节点有几个值,以及这些值的和是多少,然后,我们判断下:(假设查询前k大数的和)

若当前节点所具有的值的个数小于等于k,返回这些值的和即可,若访问到了最底层(即l=r时),我们输出k*v(v表当前节点对应的点的值),否则,我们类比kth:

若右儿子节点数量大于等于k,那么,我们向右儿子找

否则,我们加上右儿子所有节点的值,然后向左儿子找前(k-右儿子的值的个数)大的值的和即可

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+1;
struct node{
    int v,s,val;
}t[N];
int W[N<<2],S[N<<2];
int b[N],s[N];
inline int find(int now,int l,int r,int k){
    if(W[now]<=k){
        return S[now];
    }
    if(l==r){
        return s[l]*k;
    }
    int mid=(l+r)>>1;
    if(W[now<<1|1]>=k){
        return find(now<<1|1,mid+1,r,k);
    }
    return S[now<<1|1]+find(now<<1,l,mid,k-W[now<<1|1]);
}
inline void insert(int now,int l,int r,int x,int y){
    ++W[now];S[now]+=y;
    if(l==r){
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid){
        insert(now<<1,l,mid,x,y);
    }else{
        insert(now<<1|1,mid+1,r,x,y);
    }
}
inline bool kkk(node x,node y){
    return x.s<y.s;
}
signed main(){
    int n;
    scanf("%lld",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld%lld",&t[i].v,&t[i].s);
        b[i]=t[i].v;
    }
    sort(b+1,b+n+1);
    int m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i){
        t[i].val=lower_bound(b+1,b+m+1,t[i].v)-b;
        s[t[i].val]=t[i].v;
    }
    sort(t+1,t+n+1,kkk);
    int ans=0;
    for(int i=n;i;--i){//枚举最小的s
        int k=t[i].s-1;
        int res=t[i].v;
        //从i+1-n中选前k大的v
        if(k){
            res+=find(1,1,m,k);
        }
        insert(1,1,m,t[i].val,t[i].v);
        ans=max(ans,res);
    }
    printf("%lld",ans);
    return 0;
}