【BZOJ4868】期末考试(六省联考2017)-三分
测试地址:期末考试
做法:本题需要用到三分。
我们令最慢出成绩的时刻为,考虑找到:所有学科的成绩都在时刻内出的最小不满意度。
注意到不满意度可以分为两个部分:学生的不满意度和老师的不满意度。学生的不满意度十分好算,主要是老师的不满意度要怎么算。注意到,要使所有学科的成绩都在时刻前出,实际上就是把超出的时间删去或者移动到其它科的时间上去。当时,直接把超出的时间删掉即可。当时,我们要先尽可能地把超出的时间移动到其他学科上去,然后如果还有超出,就只能把超出的时间删去了。
得到了最优策略,就能算出了。注意到,学生的不满意度随着的增大而增大,并且增大的速度不断增大(可以理解为类似于导数的东西),老师的不满意度随着的增大而减小,并且减小的速度不断减小,所以它们的和就是一个下凸函数,对区间应用三分法解决即可(因为函数只有在这个区间内是一定严格递增/递减的)。计算一次的时间是的,所以总的时间复杂度是。
注意一个非常坑爹的点,有两个点,直接计算会超long long,实际上在这种情况下,学生的不满意度必须是,否则不管怎么样都无法弥补巨大的不满意度,所以直接输出即可。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll A,B,C,mint=1000000000,maxt=0,sumb=0;
ll t[100010],b[100010];
int n,m;
ll f(ll T)
{
ll ans=0,need=0,tot=0;
for(int i=1;i<=n;i++)
if (T>t[i]) ans+=C*(T-t[i]);
for(int i=1;i<=m;i++)
{
if (b[i]>T) need+=b[i]-T;
else tot+=T-b[i];
}
if (A<B)
{
ans+=A*min(need,tot);
ans+=B*max(need-tot,(ll)0);
}
else ans+=B*need;
return ans;
}
ll three_divide(ll l,ll r)
{
while(l<r-1)
{
ll mid=(l+r)/2;
ll mmid=(mid+r)/2;
if (f(mid)<f(mmid)) r=mmid;
else l=mid;
}
return f(l)<f(r)?f(l):f(r);
}
int main()
{
scanf("%lld%lld%lld",&A,&B,&C);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&t[i]);
mint=min(mint,t[i]),maxt=max(maxt,t[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%lld",&b[i]);
sumb+=b[i];
}
if (C>100000) printf("%lld",f(mint));
else printf("%lld",three_divide(mint,maxt));
return 0;
}