[六省联考 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 天公布成绩。
有如下两种操作可以调整公布成绩的时间:
- 将负责课程 X X X 的部分老师调整到课程 Y Y Y,调整之后公布课程 X X X 成绩的时间推迟一天,公布课程 Y Y Y 成绩的时间提前一天;每次操作产生 A A A 不愉快度。
- 增加一部分老师负责学科 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
(3−1)×2=4;
同学
3
3
3 希望在第
2
2
2 天或之前出成绩,产生的不愉快度为
(
3
−
2
)
×
2
=
2
(3 - 2) \times 2 = 2
(3−2)×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}
1≤n,m,ti,bi≤105,
0
≤
A
,
B
,
C
≤
1
0
16
0\le A,B,C\le10^{16}
0≤A,B,C≤1016
题解
思路
这是一道贪心的题目. 我们可以通过枚举成绩发布的最晚天数, 并计算其不愉快度(以下简称愤怒值), 从
b
m
i
n
b_{min}
bmin到
b
m
a
x
b_{max}
bmax. 现在我们考虑如何计算愤怒值
如下图:
假设我们现在枚举到了第三天 (那条红线) :
很明显, 如果要求最晚乘积不大于
3
3
3, 我们的目的就是让凸出来的部分 (绿色部分) 转移
/
/
/消失
现在对于操作操作
1
,
2
1,2
1,2我们可以做如下变形:
- 使用代价为 A A A, 将一列最上方的块移到另一列
- 使用代价为 B B B, 将一列最上方的块移除(直接消失)
显然:
- 如果 B ≤ A B\le A B≤A, 那么可以用 B B B来代替 A A A
- 如果 A ≤ B A\le B A≤B, 优先将凸出来的部分 (绿色部分) 转移到空缺的部分 (蓝色部分) .如果仍有剩余, 就用 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×(m−i最后一次出现的下标)
计算出面积后, 运用上面提到过的贪心方法,
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;
}