题解 洛谷P3745 【[六省联考2017]期末考试】

时间:2019-12-22
本文章向大家介绍题解 洛谷P3745 【[六省联考2017]期末考试】,主要包括题解 洛谷P3745 【[六省联考2017]期末考试】使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

这题有点绕,我写了\(2h\)终于搞明白了。

主要思路:枚举最晚公布成绩的时间\(maxt\),然后将所有公布时间大于\(maxt\)的课程都严格降为\(maxt\)即可。

在此之前,还要搞清楚一个概念:对于第二种操作,它只有将某一门课提前,但是第一种操作,它还会在提前的过程中延迟某一门课。所以,在不考虑代价的情况下,选择第二种操作是更优也更快捷的。

\(OK\),接下来我们就能来分情况贪心了。

  • 如果\(A>=B\),说明第一种操作比第二种操作的代价来的高,操作也没有第二种优,所以肯定优先选择第二种操作。

  • 如果\(A<B\),说明第一种操作的代价比较小,于是乎,我们可以先选第一种到不能选为止(已经没有课程可以延迟),剩余的再选第二种。

那么接下来,我们再算一下在最晚公布时间为\(i\)的情况下学生们的不愉快度即可。

实现过程

  • \(A>=B\)的情况

此时我们要计算有多少门课公布的时间大于\(i\),我们可以先考虑将所有的课程按公布时间排序,然后把公布时间比\(i\)晚的课程先计算出来,这里我就用\(x\)来表示数量,则将这些课程公布的时间记作\(a_{m-x+1},a_{m-x+2}……a_m\)

而我们的意图是将这里所有的值都化为\(i\),这其中经过的时间为\(a_{m-x+1}+a_{m-x+2}+……+a_m-i \times x\)

代价为\((a_{m-x+1}+a_{m-x+2}+……+a_m-i \times x) \times B\)

这个东西我们可以用前缀和\(O(1)\)的求出,而\(x\)的值怎么求可以参考我的代码。

  • \(A<B\)的情况

一样的,先算出要将其化为\(i\)需要经过的时间:

\(a_{m-x+1}+a_{m-x+2}+……+a_m-i \times x\) \((\)记作\(t1)\)

然后再算比\(i\)早公布时间的课程,我们可以将其延迟至\(i\)时间公布。

\(i \times (m-x) -(a_{1}+a_{2}+……+a_x)\) \((\)记作\(t2)\)

比较\(t1\)\(t2\),如果\(t1<=t2\),说明可以全部用第一种操作搞定,代价为:\(t1 \times A\)

否则,剩余的再用第二种操作,代价为:\(t2 \times A+(t1-t2) \times B\)

  • 学生的不愉快度

我们可以用一个\(Num\)记录在最晚公布时间为\(i\)的情况下不愉快的学生,将\(t_i\)从小到大排序,则不愉快的学生的等待时间记为:\(t_1,t_2,t_3……t_{Num}\)

总等待时间为:

\(i \times Num-(t_1+t_2+t_3……+t_{Num})\)

不愉快度为:

\((i \times Num-(t_1+t_2+t_3……+t_{Num})) \times C\)

最后注意用\(unsigned\) \(long\) \(long\)哦~

\(Code:\)

#include<bits/stdc++.h>
#define long unsigned long long 
using namespace std;
inline int read(){
    register int s=0,f=1;
    register char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f*=-1;ch=getchar();}
    while(isdigit(ch))s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
    return s*f;
}
const int max_n=100000+5;
long late[max_n],Time[max_n];
long sum[max_n],f[max_n],num[max_n],h[max_n],maxt;
long Max(long a,long b){
    if(a>b)return a;
    return b;
}
long Min(long a,long b){
    if(a<b)return a;
    return b;
}
int main(){
    ios::sync_with_stdio(false);
    long A=read(),B=read(),C=read();
    long n=read(),m=read(); 
    for(int i=1;i<=n;i++)late[i]=read();
    for(int i=1;i<=m;i++)Time[i]=read();//读入 
    sort(Time+1,Time+m+1);//排序 
    for(int i=1;i<=m;i++){
        maxt=max(maxt,Time[i]),sum[Time[i]]++;
        //找最晚公布的时间        //记录当前值,方便统计 
        f[i]=f[i-1]+Time[i];//求前缀和 
    }
    sort(late+1,late+n+1);//排序 
    long Num=0;//不愉快的学生 
    for(int i=1;i<=n;i++){
        num[late[i]]++,h[i]=h[i-1]+late[i];
        if(late[i]<=maxt)Num=i; 
    }
    long ans=LONG_LONG_MAX;
    long x=0;//公布时间比i晚的课程 
    for(int i=maxt;i>=1;i--){
        Num-=num[i];
        long tot=0;
        if(A>=B){
            long day=f[m]-f[m-x]-x*i; 
            tot=Max(day*B,0);
        }else{
            long day1=f[m]-f[m-x]-x*i;
            long day2=(m-x)*i-f[m-x];
            if(day1<=day2)tot=Max(day1*A,0); 
            else tot=Max(day2*A+(day1-day2)*B,0);
        }//贪心的核心部分,具体见分析,注意要严格保证不能为负数 
        tot+=Max(Num*i-h[Num],0)*C;//加上学生的不愉快度 
        x+=sum[i];//更新 
        ans=Min(ans,tot);//取最小 
    }
    cout<<ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/Agonim/p/12080797.html