思路
题意是给你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,n−1]
,其对应的人都会捐a[i]元,用前缀和优化下直接求区间和即可。剩下的人则都会捐b[i]元,故总数
ans=∑n−1j=pa[j]+b[i]∗(p−i);
,减去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;
}