zoukankan      html  css  js  c++  java
  • 题解——[六省联考2017]期末考试(模拟+递推)

    题解——[六省联考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 ;
    }
    

    如有不足,请大佬指出

  • 相关阅读:
    list转datatable c#
    按钮靠右css小结
    IE浏览器打印合格证相关问题
    vue项目插入视频-mp4
    vue项目bug-Couldn’t find preset "es2015"
    Mac打开swf文件
    mac+windows下从git上拉取项目及运行
    echarts.js制作中国地图
    前端数据可视化echarts.js
    vue-router 基本使用
  • 原文地址:https://www.cnblogs.com/ssw02/p/11536513.html
Copyright © 2011-2022 走看看