整数三分—期末考试

直达原题

题目描述

\(n\) 位同学,每位同学都参加了全部的 \(m\) 门课程的期末考试,都在焦急的等待成绩的公布。

\(i\) 位同学希望在第 \(t_i\) 天或之前得知所有课程的成绩。如果在第 \(t_i\) 天,有至少一门课程的成绩没有公布,他就会等待最后公布成绩的课程公布成绩,每等待一天就会产生 \(C\) 不愉快度。

对于第 \(i\) 门课程,按照原本的计划,会在第 \(b_i\) 天公布成绩。

有如下两种操作可以调整公布成绩的时间:

  1. 将负责课程 \(X\) 的部分老师调整到课程 \(Y\),调整之后公布课程 \(X\) 成绩的时间推迟一天,公布课程 \(Y\) 成绩的时间提前一天;每次操作产生 \(A\) 不愉快度。
  2. 增加一部分老师负责学科 \(Z\),这将导致学科 \(Z\) 的出成绩时间提前一天;每次操作产生 \(B\) 不愉快度。

上面两种操作中的参数 \(X, Y, Z\) 均可任意指定,每种操作均可以执行多次,每次执行时都可以重新指定参数。

现在希望你通过合理的操作,使得最后总的不愉快度之和最小,输出最小的不愉快度之和即可。

输入格式

第一行三个非负整数 \(A, B, C\),描述三种不愉快度,详见【题目描述】;
第二行两个正整数 \(n, m\),分别表示学生的数量和课程的数量;
第三行 \(n\) 个正整数 \(t_i\),表示每个学生希望的公布成绩的时间;
第四行 \(m\) 个正整数 \(b_i\),表示按照原本的计划,每门课程公布成绩的时间。

输出格式

输出一行一个整数,表示最小的不愉快度之和。

样例 #1

样例输入 #1

100 100 2
4 5
5 1 2 3
1 1 2 3 3

样例输出 #1

6

样例 #2

样例输入 #2

3 5 4
5 6
1 1 4 7 8
2 3 3 1 8 2

样例输出 #2

33

提示

样例解释 1

由于调整操作产生的不愉快度太大,所以在本例中最好的方案是不进行调整;全部的 \(5\) 门课程中,最慢的在第 \(3\) 天出成绩;
同学 \(1\) 希望在第 \(5\) 天或之前出成绩,所以不会产生不愉快度;
同学 \(2\) 希望在第 \(1\) 天或之前出成绩,产生的不愉快度为 \((3 - 1) \times 2 = 4\)
同学 \(3\) 希望在第 \(2\) 天或之前出成绩,产生的不愉快度为 \((3 - 2) \times 2 = 2\)
同学 \(4\) 希望在第 \(3\) 天或之前出成绩,所以不会产生不愉快度;
不愉快度之和为 \(4 + 2 = 6\)

数据范围

Case # \(n, m, t_i, b_i\) \(A, B, C\)
1, 2 \(1 \leq n, m, t_i, b_i \leq 2000\) \(A = 10^9; B = 10^9; 0 \leq C \leq 10^2\)
3, 4 \(1 \leq n, m, t_i, b_i \leq 2000\) \(0 \leq A; C \leq 10^2; B = 10^9\)
5, 6, 7, 8 \(1 \leq n, m, t_i, b_i \leq 2000\) \(0 \leq B \leq A \leq 10^2; 0 \leq C \leq 10^2\)
9 - 12 \(1 \leq n, m, t_i, b_i \leq 2000\) \(0 \leq A, B, C \leq 10^2\)
13, 14 \(1 \leq n, m, t_i, b_i \leq 10^5\) \(0 \leq A, B \leq 10^5; C = 10^{16}\)
15 - 20 \(1 \leq n, m, t_i, b_i \leq 10^5\) \(0 \leq A, B, C \leq 10^5\)

分析

题目是求最小不愉快度,给出的关键变量就是时间,和各种行为所产生的不愉快度,这道题的关键就是合理的安排各科成绩的出分时间去使不愉快的最小,我们注意到如果最后一门成绩出来的时间被要求的越早且早于学生希望时间(相对于既定的时间)那么老师不愉快度比较大,也就是总的不愉快度大,此时如果我们再宽限老师几天但还是早于学生希望时间那么不愉快度就会下降,如果再宽限超过学生希望时间,那么不愉快度就会上升,所以不愉快度是时间的下凹函数也就是单谷函数,此时我们就可以用三分法整数三分法去求极值。

代码实现

#include<bits/stdc++.h>
const int N = 100005;
using namespace std;
typedef long long ll;
int n,m,t[N],b[N];
ll A,B,C,ans;
ll calc1(int p){             //计算通过A,B操作把时间调到p的不愉快度
    ll x=0,y=0;
    for(int i=1;i<=m;i++){
        if(b[i]<p) x += p-b[i];
        else       y += b[i]-p;
    }
    if(A<B) return min(x,y)*A+(y-min(x,y))*B;  //A<B,先找在希望时间里的科目,让其迟出一天使得希望时间外的科目早出一天,希望时间里的科目不够时再用B
    else return y*B;                           //B<=A,直接全部使用B
}
ll calc2(int p)  {           //计算学生们的不愉快度总和
    ll sum=0;
    for(int i=1;i<=n;i++)  
        if(t[i]<p) sum += (p-t[i])*C;
    return sum;
}
int main(){
    cin>>A>>B>>C>>n>>m;
    for(int i=1;i<=n;i++) cin >> t[i];
    for(int i=1;i<=m;i++) cin >> b[i];
    sort(b+1,b+m+1);  sort(t+1,t+n+1);
    if(C>=1e16) { cout<<calc1(t[1])<<endl; return 0;}   //一个特判,我们看数据范围#13,14中发现C远远大于A,B所以我们不能让学生有任何不愉快度
    ans=1e16;
    int l=1,r=N;      //left, right
    while(r-l > 2){   //把2改成其他数字也行,后面的for再找最小值
         int mid1 = l+(r-l)/3;  int mid2 = r-(r-l)/3;
         ll c1 = calc1(mid1) + calc2(mid1);   //总不愉快度
         ll c2 = calc1(mid2) + calc2(mid2);
         if (c1<=c2) r = mid2;
         else        l = mid1;
    }
    for(int i=l;i<=r;i++){    //在上面求出的区间内再枚举时间求出最小值
        ll x = calc1(i) + calc2(i);
        ans = min(ans,x);
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2023-07-27 23:26  LongDz  阅读(71)  评论(0)    收藏  举报