题解——[六省联考2017]期末考试(模拟+递推)

要写数据分治,但这道题真的水


Description

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

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

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

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

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

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

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

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

Input

从文件exam.in 中读入数据。
第一行三个非负整数A; B;C ,描述三种不愉快度,详见【问题描述】;
第二行两个正整数 n m ,分别表示学生的数量和课程的数量;
第三行n 个正整数ti ,表示每个学生希望的公布成绩的时间;
第四行m 个正整数bi ,表示按照原本的计划,每门课程公布成绩的时间。

Output

输出到文件exam.out 中。
输出一行一个整数,表示最小的不愉快度之和。

in.1
100 100 2
4 5
5 1 2 3
1 1 2 3 3

out.1
6

数据范围与约定

一些性质

1.由题意得,其实影响最后答案的科目一定是排在最后面的科目。

2.如果我们将最后同一时刻上的科目往前移动,而没有移动在这一时刻上的所有科目,那么这之前的移动是无效的。

3.当不考虑特殊条件时,如果 A < B 那么优先选 A 操作,再选 B。否则全部选 B 操作即可。

思考

如果我们枚举时刻 i ,考虑将所有在时刻 i 之后的课程提到 i 时刻来,那么对于时刻 i 而言,这一定是当前时刻的最优解。

发现数据很大,所以我们要预处理出将所有在时刻 i 之后的科目提到 i 时刻后,学生的不满意度 和 提前操作的不满意度 。

由于 A 操作的次数在每个时刻 i 是有限的(不能把科目延迟到 i 之后),我们也要预处理出来。

主要分为两部分:

1.预处理出我们需要的数组。

2.O(n)枚举时间,取最小答案即可(包括一些特判)

处理:

1.我们用2个桶 t[ ] 和 d[ ] 分别记录在 i 时刻学生的时间上限为i的数量结束科目的数量
顺便记录一下最后一科的时间,作为枚举边界。

for( int i = 1 ; i <= N ; ++i )m1 = (int)read() , t[ m1 ]++ ;
for( int i = 1 ; i <= M ; ++i )m1 = (int)read() , las = max( (ll)m1 , las ) , d[ m1 ]++ ;

然后我们枚举时刻 i ,直接预处理出上述三个需要的数组。记录方法为先累加贡献值,再累加个数,自己想想其实很简单的。复杂度 O(n)。

ll tot = 0 , val = 0 ;//∑C
if( flag != 3 )
    for( int i = 1 ; i <= las ; ++i )
        val += tot*C , tot += t[ i ] , c[ i ] = val ;
tot = 0 , val = 0 ;
for( int i = 1 ; i <= las ; ++i )//A的上限
    val += tot , tot += d[ i ] , used[ i ] = val ;
tot = 0 , val = 0 ;
for( int i = las ; i >= 1 ; --i )//需要提前的次数
    val += tot , tot += d[ i ] , cha[ i ] = val ;

2.预处理完成之后,我们枚举时间,求出学生的不满意度和提前操作的不满意度之和即可。

特殊点处理:

1 2 点不能使用 A,B ,直接输出 c[ las ]
3 4 点不能使用 B , 全部使用 A , A不够直接 break
13 14 点不能预处理 C , 也不能使用 C , 直接求出提前到最早时间要求处的答案即可。

时间复杂度

综上所述,预处理和递推模拟都是严格 O(n),而且数据分治,复杂度已经优化,在数据更新后20个点只跑了134ms,比大部分数据更新前的人都跑的快。

#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 100005 ;
inline ll read(){
    ll s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
    while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
ll t[ MAXN ] , d[ MAXN ] , used[ MAXN ] , cha[ MAXN ] , c[ MAXN ] ;
ll A , B , C , N , M , ans , las ;//las记录最后一科出来的时间
int  flag = 0 , flag2 = 0 ;
int check(){
    if( A < B )flag2 = 1 ;
    if( A == 1e9 && B == 1e9 )return 1 ;
    if( A != 1e9 && B == 1e9 )return 2 ;
    if( C == 1e16 )return 3 ;
    return 0 ;
}
void  prepare(){
    ll tot = 0 , val = 0 ;//∑C
    if( flag != 3 )
        for( int i = 1 ; i <= las ; ++i )
            val += tot*C , tot += t[ i ] , c[ i ] = val ;
    tot = 0 , val = 0 ;
    for( int i = 1 ; i <= las ; ++i )//A的上限
        val += tot , tot += d[ i ] , used[ i ] = val ;
    tot = 0 , val = 0 ;
    for( int i = las ; i >= 1 ; --i )
        val += tot , tot += d[ i ] , cha[ i ] = val ;
}
void work(){
    ll now ; ans = c[ las ] ;
    for( int i = las ; i >= 1 ; --i ){
        now = c[ i ] ;
        if( flag2 ){//优先用A
            if( used[ i ] >= cha[ i ] )now += cha[ i ]*A ;
            else now += used[ i ]*A + ( cha[ i ] - used[ i ] )*B ;
        }
        else
            now += cha[ i ]*B ;
        ans = min( ans , now ) ;
    }
    cout<<ans ;
}
void work2(){//仅使用 A C
    ll now ; ans = c[ las ] ;
    for( int i = las ; i >= 1 ; --i ){
        now = c[ i ] ;
        if( used[ i ] >= cha[ i ] )now += cha[ i ]*A ;
        else break ;
        ans = min( ans , now ) ;
    }
    cout<<ans ;
}
void work3(){//仅使用 A B
    ans = 0LL ; //上限1e10
    ll fir = 0LL ;
    for( int i = 1 ; i <= las ; ++i )if( t[ i ] ){ fir = i ; break ; }
    if( flag2 ){//优先用A
            if( used[ fir ] >= cha[ fir ] )ans += cha[ fir ]*A ;
            else ans += used[ fir ]*A + ( cha[ fir ] - used[ fir ] )*B ;
        }
    else
        ans += cha[ fir ]*B ;
    cout<<ans ;
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    A = read() , B = read() , C = read() , N = read() , M = read() ;
    int m1 , m2 ;
    for( int i = 1 ; i <= N ; ++i )m1 = (int)read() , t[ m1 ]++ ;
    for( int i = 1 ; i <= M ; ++i )m1 = (int)read() , las = max( (ll)m1 , las ) , d[ m1 ]++ ;
    flag = check() ;
    prepare() ;
    if( flag == 1 ){cout<<c[ las ] ; return 0 ; }
    if( flag == 2 ){ work2() ; return 0 ; }
    if( flag == 3 ){ work3() ; return 0 ; }
    work() ;
    return 0 ;
}

如有不足,请大佬指出

01-18 10:41