来补题辣~
这题很简单,我们来判断下一个选择的序列是否合法的充要条件是什么:
合法是要满足对于所有的选择进去的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;
} 
京公网安备 11010502036488号