寒假集训 1007

思路

题意是给你n个人的捐钱区间[a,b],叫你设定一个值x,如果x < a,则那个人会捐a元,如果x在[a,b]内,会捐x元,否则如果x > b,就不捐。
现在求一个x使得捐钱总数最大。

当x逐渐增大时,可以发现总数减小的时刻仅为b[i],即b[i]为总数的极值点。
所以把a[i],b[i]增序排好,
然后历遍b[i],对于每个b[i],用二分查找找出a[i]中第一个比它大的元素下标p,那么对于所有 a[i],i[p,n1] ,其对应的人都会捐a[i]元,用前缀和优化下直接求区间和即可。剩下的人则都会捐b[i]元,故总数 ans=n1j=pa[j]+b[i](pi); ,减去i是因为每遍历一个b[i],这个b[i]对应的人就得减掉。
由此可以写出代码1。时间复杂度3nlogn。

然后每次都二分查找没必要,可以继续优化,每次判断当前b[i]有没有比a[p]大,大的话一直往前进就行。
由此改进出代码2,时间复杂度2nlogn+2n。

AC代码1

#include <bits/stdc++.h>
using namespace std;

const int MAX=100000+100;
typedef long long ll;
ll a[MAX],b[MAX],sum[MAX];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0 ; i<n ; ++i)
        {
            scanf("%lld %lld",&a[i],&b[i]);
        }
        sort(a,a+n);
        sort(b,b+n);
        sum[0]=a[0];
        for(int i=1 ; i<n ; ++i)
        {
            sum[i]=sum[i-1]+a[i];
        }
        ll max_ans=0,max_i=-1;
        for(int i=0 ; i<n ; ++i)
        {
            int p=lower_bound(a,a+n,b[i])-a;
            ll ans=sum[n-1]-sum[p-1]+b[i]*(p-i);
            if(ans>max_ans)
            {
                max_ans=ans;
                max_i=i;
            }
        }
        printf("%lld %lld\n",b[max_i],max_ans);
    }
    return 0;
}

AC代码2

#include <bits/stdc++.h>
using namespace std;

const int MAX=100000+100;
typedef long long ll;
ll a[MAX],b[MAX],sum[MAX];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0 ; i<n ; ++i)
        {
            scanf("%lld %lld",&a[i],&b[i]);
        }
        sort(a,a+n);
        sort(b,b+n);
        sum[0]=a[0];
        for(int i=1 ; i<n ; ++i)
        {
            sum[i]=sum[i-1]+a[i];
        }
        ll max_ans=0,max_i=-1;
        int p=lower_bound(a,a+n,b[0])-a;
        for(int i=0 ; i<n ; ++i)
        {
            while(p+1<n && b[i]>a[p])
            {
                p++;
            }
            if(p==n-1 && b[i]>a[p])
            {
                p=n;
            }
            ll ans=sum[n-1]-sum[p-1]+b[i]*(p-i);
            if(ans>max_ans)
            {
                max_ans=ans;
                max_i=i;
            }
        }
        printf("%lld %lld\n",b[max_i],max_ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值