[SHOI2017]期末考试

 题目描述

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

第i位同学希望在第ti天或之前得知所有课程的成绩。如果在第ti天,有至少一门课程的成绩没有公布,他就会等待最后公布成绩的课程公布成绩,每等待一天就会产生C不愉快度。对于第i门课程,按照原本的计划,会在第bi天公布成绩。

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

1、将负责课程X的部分老师调整到课程Y,调整之后公布课程X成绩的时间推迟一天,公布课程Y成绩的时间提前一天;每次操作产生A不愉快度。

2、增加一部分老师负责学科Z,这将导致学科Z的出成绩时间提前一天;每次操作产生B不愉快度。

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

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

输入:

第一行三个非负整数 A; B; C ,描述三种不愉快度,详见【问题描述】;

第二行两个正整数 n; m(1 ≤ n; m ≤ 10^5) ,分别表示学生的数量和课程的数量;

第三行 n 个正整数 ti ,表示每个学生希望的公布成绩的时间;

第四行 m 个正整数 bi ,表示按照原本的计划,每门课程公布成绩的时间。

输出:

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

这道题好多其他人都写的三分,我自己在做的时候还是想的是枚举加前缀和乱搞...

首先我们只关心最后一个成绩公布的时间,所以我们可以先对人和成绩排序,再求前缀和。

这样有什么好处呢,当我们想要求当最后一个成绩公布的时间为k时调整老师的代价时,我们可以先二分划分出公布时间在时限范围内的和不在时限范围内的科目,然后利用前缀和算出需要节约出多少个单位的时间,然后有一个策略显然:

对于已经在时限内的科目,他们的公布时间具体是多少没有意义,但是当1操作的代价低于2操作的代价时,我们可以尽量让这些科目的老师去支援那些后面的老师。

设最后一个成绩是ti,恰好前pos个课程结束的时间在时限范围之内,我们最多可以进行(pos*ti-pre_class[pos])次1操作。

所以对于那些超出时限的科目,他们超出的那些时间单位的前(pos*ti-pre_class[pos])的部分可以通过1、2两种操作中代价最小的进行处理,剩下的部分只能通过2操作进行处理。

通过同样的方法,我们可以算出所有人要等的时间之和。

由于每一个人要求的时间和成绩公布的时间都不超过1e5,所以就可以枚举时间(或者三分),并在O(logn)时间内算出答案,求最小值即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 200040
#define inf (1e15)
using namespace std;
LL read(){
    LL nm=0,fh=1;char cw=getchar();
    for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    return nm*fh;
}
LL n,m,A,B,C,p[M],t[M],c[M],pre[M],cst,ps;
LL res,maxn,st[M],num,pos,sum,ans;
LL find(LL ti){
    LL L=1,R=m,tk=0,mid;
    while(L<=R){
        mid=(L+R>>1);
        if(p[mid]<=ti) tk=mid,L=mid+1;
        else R=mid-1;
    }
    return tk;
}
LL findt(LL ti){
    LL L=1,R=n,tk=0,mid;
    while(L<=R){
        mid=(L+R>>1);
        if(t[mid]<ti) tk=mid,L=mid+1;
        else R=mid-1;        
    }
    return tk;
}
LL gt_ans(LL ti){
    pos=find(ti),num=pre[m]-pre[pos]-((m-pos)*ti);
    res=(pos*ti)-pre[pos],ps=findt(ti);
    if(ti>=t[1]) sum=(ps*ti-st[ps])*C;
    else sum=0;
    if(num<=res) sum+=num*min(A,B);
    else sum+=(num-res)*B+res*min(A,B);
    return sum;
}
int main(){
    A=read(),B=read(),C=read();
    n=read(),m=read();
    memset(&ans,0x7f,sizeof(LL));
    for(LL i=1;i<=n;i++) t[i]=read();
    for(LL i=1;i<=m;i++) p[i]=read();
    sort(t+1,t+n+1),sort(p+1,p+m+1),maxn=p[m];
    for(LL i=1;i<=n;i++) st[i]=st[i-1]+t[i];
    for(LL i=1;i<=m;i++) pre[i]=pre[i-1]+p[i];
    if(C>inf) ans=gt_ans(t[1]);
    else for(LL i=1;i<=maxn;i++) cst=gt_ans(i),ans=min(ans,cst);
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/OYJason/p/8722444.html