[luogu] P3745 [六省联考2017]期末考试 (贪心)

P3745 [六省联考2017]期末考试

题目描述

(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: 复制

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

输出样例#1: 复制

6

输入样例#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) ∗ 2 = 4;

同学 3 希望在第 2 天或之前出成绩,产生的不愉快度为 (3 − 2) ∗ 2 = 2;

同学 4 希望在第 3 天或之前出成绩,所以不会产生不愉快度;

不愉快度之和为 4 + 2 = 6 。

题解

整个题目的贪心在于如何处理(A<B),因为,(A)的使用会伴随着一门科目的推迟,也就是说(A)的使用次数是有限的。

注意到整个题目的意思就是要我们提前天数,那么我们可以考虑枚举天数,网上使用三分和二分的大佬们实在是太巨辣,我只会(O(n))
那么当(A<B)的时候,我们就根据天数的推移用前缀和统计一下能有几天可以推迟,然后计算(A)使用的次数,不够的再用(B)提前。
整个难点就在于学生和科目随时间的线性变化,要自己理一理。
我们用一个桶记录学生忍耐的时间,另一个记录科目出成绩的时间。
那么随时间线性变化便是,
每往后一天,记录这天前有多少科目是可以推迟的,对于推迟的贡献,
有多少学生是开始忍耐不了的。
有多少科目是还需要提前的,对于提前一共要多少天。
自己好好想一下。不懂可以看代码。

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define ll unsigned long long
using namespace std;
const ll MAXN=1e6+5;
ll a,b,c,n,m,N,M,sum,ans=1e18,tot;
ll ti[MAXN],bi[MAXN],bac1[MAXN],bac2[MAXN];
ll s,ss,tt,t1,t2,t3;
ll read(){
    ll x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}
int main(){
    a=read();b=read();c=read();
    n=read();t2=m=read();
    for(ll i=1;i<=n;i++)ti[i]=read(),bac1[ti[i]]++;
    for(ll i=1;i<=m;i++)bi[i]=read(),bac2[bi[i]]++,ss+=bi[i];
    for(ll i=1;i<=100000;i++){
        //tt当前不能忍受的人的天数,ss当前需要提前的天数,s是当前可以做贡献的天数
        tt+=bac1[i-1]+t1;t1+=bac1[i-1];
        t2-=bac2[i];ss-=bac2[i]+t2;
        s+=bac2[i-1]+t3;t3+=bac2[i-1];
        sum=tt*c;tot=0;
        if(a<b){
            tot=min(ss,s);sum+=min(ss,s)*a;
        }
        sum=sum+max((ss-tot),(ll)0)*b;
        ans=min(ans,sum);
    }
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/hhh1109/p/10667144.html