P3745 [六省联考 2017] 期末考试题解__贪心

[六省联考 2017] 期末考试

题目描述

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

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

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

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

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

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

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

输入格式

第一行三个非负整数 A , B , C A, B, C A,B,C,描述三种不愉快度,详见【题目描述】;
第二行两个正整数 n , m n, m n,m,分别表示学生的数量和课程的数量;
第三行 n n n 个正整数 t i t_i ti,表示每个学生希望的公布成绩的时间;
第四行 m m m 个正整数 b i b_i bi,表示按照原本的计划,每门课程公布成绩的时间。

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

6

样例 #2

样例输入 #2

3 5 4
5 6
1 1 4 7 8
2 3 3 1 8 2

样例输出 #2

33

提示

样例解释 1

由于调整操作产生的不愉快度太大,所以在本例中最好的方案是不进行调整;全部的 5 5 5 门课程中,最慢的在第 3 3 3 天出成绩;
同学 1 1 1 希望在第 5 5 5 天或之前出成绩,所以不会产生不愉快度;
同学 2 2 2 希望在第 1 1 1 天或之前出成绩,产生的不愉快度为 ( 3 − 1 ) × 2 = 4 (3 - 1) \times 2 = 4 (31)×2=4
同学 3 3 3 希望在第 2 2 2 天或之前出成绩,产生的不愉快度为 ( 3 − 2 ) × 2 = 2 (3 - 2) \times 2 = 2 (32)×2=2
同学 4 4 4 希望在第 3 3 3 天或之前出成绩,所以不会产生不愉快度;
不愉快度之和为 4 + 2 = 6 4 + 2 = 6 4+2=6

数据范围

让我们周老师做一下数据加强吧…
对于100%的数据, 1 ≤ n , m , t i , b i ≤ 1 0 5 1\le n,m,t_i,b_i\le10^{5} 1n,m,ti,bi105, 0 ≤ A , B , C ≤ 1 0 16 0\le A,B,C\le10^{16} 0A,B,C1016

题解

思路

这是一道贪心的题目. 我们可以通过枚举成绩发布的最晚天数, 并计算其不愉快度(以下简称愤怒值), 从 b m i n b_{min} bmin b m a x b_{max} bmax. 现在我们考虑如何计算愤怒值
如下图:
在这里插入图片描述

假设我们现在枚举到了第三天 (那条红线) :
很明显, 如果要求最晚乘积不大于 3 3 3, 我们的目的就是让凸出来的部分 (绿色部分) 转移 / / /消失
现在对于操作操作 1 , 2 1,2 1,2我们可以做如下变形:

  1. 使用代价为 A A A, 将一列最上方的块移到另一列
  2. 使用代价为 B B B, 将一列最上方的块移除(直接消失)

显然:

  1. 如果 B ≤ A B\le A BA, 那么可以用 B B B来代替 A A A
  2. 如果 A ≤ B A\le B AB, 优先将凸出来的部分 (绿色部分) 转移到空缺的部分 (蓝色部分) .如果仍有剩余, 就用 2 2 2号操作消除

现在的问题就是, 我们如何快速求出突出部分和空缺部分(绿色和蓝色)的面积呢
我们可以使用前缀和来优化.
我们发现, 真正计算的时候, 愤怒值与原序列的排列顺序无关, 所以我们可以对它从小到大排序, 之后再记录前缀和.
仍然如上图, 对于枚举到了天数 i i i, 空缺(蓝色部分)的面积为
i × ( i 第一次出现的下标 − 1 ) − s u m b [ i 第一次出现的下标 − 1 ] i\times (i第一次出现的下标-1)-sumb[i第一次出现的下标-1] i×(i第一次出现的下标1)sumb[i第一次出现的下标1]
同理, 突出(绿色部分)的面积为:
s u m b [ m ] − s u m b [ i 最后一次出现的下标 ] − i × ( m − i 最后一次出现的下标 ) sumb[m]-sumb[i最后一次出现的下标]-i\times (m-i最后一次出现的下标) sumb[m]sumb[i最后一次出现的下标]i×(mi最后一次出现的下标)
计算出面积后, 运用上面提到过的贪心方法, O ( 1 ) O(1) O(1)计算即可
运用相同的办法, 对学生的期望天数求前缀和, 同理求出学生的愤怒值即可
最后统计以 i i i为成绩发布的最晚天数, 取最小值

AC Code

#include <bits/stdc++.h>
using namespace std;
#define int __int128

inline __int128 read(){
    __int128 x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void print(__int128 x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}


int A,B,C;
int n,m;
int t[100010],b[100010];
int dp[100010];
int sumt[100010],sumb[100010];
int maxn=-1,minx=100001;


int front(int num,int maxn,int a[]){
	int l=1,r=maxn;
	int cnt=0;
	while(l<r){
		int mid=(l+r)>>1;
		if(a[mid]>=num){
			r=mid;
		}else{
			l=mid+1;
		}
		cnt++;
		if(cnt>=33){
			break;
		}
	}
	return l-1;
}

int back(int num,int maxn,int a[]){
	return upper_bound(a+1,a+maxn+1,num)-a-1;
}



int find(int date){
	int ans=0;
	if(B<=A){
		
		
		int l=front(date,m,b);
		int r=back(date,m,b);
		int kong=date*l-sumb[l];
		int tuchu=(sumb[m]-sumb[r])-date*(m-r);
		if(tuchu<=0){
			tuchu=0;
		}
		ans+=tuchu*B;
		
		
	}else{
		
		
		int l=front(date,m,b);
		int r=back(date,m,b);
		int kong=date*l-sumb[l];
		int tuchu=(sumb[m]-sumb[r])-date*(m-r);
		if(tuchu<=0){
			tuchu=0;
		}
		
		
		if(kong>=tuchu){
			ans+=tuchu*A;
		}else{
			ans+=kong*A;
			ans+=(tuchu-kong)*B;
		}
	}
	int l=front(date,n,t);
	ans+=(date*l-sumt[l])*C;
	return ans;
}



signed main(){
	A=read();
	B=read();
	C=read();
	n=read();
	m=read();
	for(int x=1;x<=n;x++){
		t[x]=read();
	}
	for(int x=1;x<=m;x++){
		b[x]=read();
		maxn=max(maxn,b[x]);
		minx=min(minx,b[x]);
	}
	
	sort(t+1,t+1+n);
	sort(b+1,b+1+m);
	for(int x=1;x<=n;x++){
		sumt[x]=sumt[x-1]+t[x];
	}
	for(int x=1;x<=m;x++){
		sumb[x]=sumb[x-1]+b[x];
	}
	
	for(int x=maxn;x>=minx;x--){
		dp[x]=find(x);
	}
	int ans=1e35;
	for(int x=minx;x<=maxn;x++){
		ans=min(ans,dp[x]);
	}
	print(ans);
	return 0;
} 
  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值