【BZOJ4868】期末考试(六省联考2017)-三分

测试地址:期末考试
做法:本题需要用到三分。
我们令最慢出成绩的时刻为T,考虑找到f(T):所有学科的成绩都在时刻T内出的最小不满意度。
注意到不满意度可以分为两个部分:学生的不满意度和老师的不满意度。学生的不满意度十分好算,主要是老师的不满意度要怎么算。注意到,要使所有学科的成绩都在时刻T前出,实际上就是把超出的时间删去或者移动到其它科的时间上去。当AB时,直接把超出的时间删掉即可。当A<B时,我们要先尽可能地把超出的时间移动到其他学科上去,然后如果还有超出,就只能把超出的时间删去了。
得到了最优策略,就能算出f(T)了。注意到,学生的不满意度随着T的增大而增大,并且增大的速度不断增大(可以理解为类似于导数的东西),老师的不满意度随着T的增大而减小,并且减小的速度不断减小,所以它们的和f(T)就是一个下凸函数,对区间[min(ti),max(ti)]应用三分法解决即可(因为函数只有在这个区间内是一定严格递增/递减的)。计算一次f(T)的时间是O(n)的,所以总的时间复杂度是O(nlogn)
注意一个非常坑爹的点,有两个点C=1016,直接计算会超long long,实际上在这种情况下,学生的不满意度必须是0,否则不管怎么样都无法弥补巨大的不满意度,所以直接输出f(min(ti))即可。
以下是本人代码:

#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;
}
posted @ 2018-04-19 17:25  Maxwei_wzj  阅读(85)  评论(0编辑  收藏  举报