tokitsukaze and Soldier

传送门

题目描述

在一个游戏中,tokitsukaze需要在n个士兵中选出一些士兵组成一个团去打副本。
第i个士兵的战力为v[i],团的战力是团内所有士兵的战力之和。
但是这些士兵有特殊的要求:如果选了第i个士兵,这个士兵希望团的人数不超过s[i]。(如果不选第i个士兵,就没有这个限制。)
tokitsukaze想知道,团的战力最大为多少。

输入描述:

第一行包含一个正整数n(1≤n≤10^5)。
接下来n行,每行包括2个正整数v,s(1≤v≤10^9,1≤s≤n)。

输出描述:

输出一个正整数,表示团的最大战力。

示例1

输入

2

1 2

2 2

输出

3

示例2

输入

3

1 3

2 3

100 1

输出

100

刚学完权值线段树就碰到这么一道题。。。

思路

当我们选择3个士兵的时候,所有限制条件大于等于3的士兵都可以选择,那么我们肯定选择3个权值最大的。
所以可以枚举选取士兵的个数,求前k大士兵的权值和。(求前k个,就想到权值线段树了)

本来用的multiset,超时了,就写了线段树。

首先将数据离散化,按照限制条件从大到小排序,维护一个权值线段树,每个节点维护当前区间的数出现的次数以及总和,然后将限制条件相同的士兵的权值更新到线段树中,查询当前树中的前k大的和,注意可能有多个相同的权值,所以写查询的时候注意一下。

代码

#include<bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<set>
using namespace std;
const int N=1e5+10;
typedef long long ll;
struct note//保存士兵信息
{
    ll val,num;
} arr[N];
struct T
{
    ll l,r,size,pre;
} node[N*10];//线段树
bool cmp(note a,note b)
{
    return a.num>b.num;
}
vector<ll>v;
ll getid(ll x)//离散化
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void update(ll rt,ll l,ll r,ll val,ll real)//更新,val是离散化后的值,real是原来的值
{
    node[rt].pre+=real,node[rt].size++;
    if(l==r) return ;
    ll mid=(l+r)/2;
    if(val<=mid) update(rt*2,l,mid,val,real);
    else update(rt*2+1,mid+1,r,val,real);
}
ll query(ll rt,ll l,ll r,ll k)//查询
{
    if(node[rt].size<=k) return node[rt].pre;
    if(node[rt].size>k&&l==r) return k*(node[rt].pre/node[rt].size);//多个权值相同的时候
    ll mid=(l+r)/2;
    ll len=node[rt*2+1].size;//优先向右查询
    if(k<=len) return query(rt*2+1,mid+1,r,k);
    else return node[rt*2+1].pre+query(rt*2,l,mid,k-len);
}
int main()
{
    ll n;
    scanf("%lld",&n);
    for(ll i=1; i<=n; i++) scanf("%lld%lld",&arr[i].val,&arr[i].num),v.push_back(arr[i].val);
    sort(arr+1,arr+1+n,cmp),sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end());
    ll size=v.size(),ans=0;
    for(ll i=1; i<=n; i++)
    {
        ll now=arr[i].num;//当前选取士兵的个数
        ll j=i;
        while(j<=n&&arr[j].num==now)//相同限制条件全部更新
            update(1,1,size,getid(arr[j].val),arr[j].val),j++;
        i=j-1;
        ans=max(ans,query(1,1,size,now));
    }
    printf("%lld\n",ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值