1. 程式人生 > >[Shoi2017]期末考試

[Shoi2017]期末考試

輸入 ont 需要 std tdi 個人 blog 表示 d+

題目描述

有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\n",ans);
    return 0;
}

[Shoi2017]期末考試