BZOJ4868: [Shoi2017]期末考试
Submit: 936 Solved: 426
[Submit][Status][Discuss]
Description
有n位同学,每位同学都参加了全部的m门课程的期末考试,都在焦急的等待成绩的公布。第i位同学希望在第ti天
或之前得知所.有.课程的成绩。如果在第ti天,有至少一门课程的成绩没有公布,他就会等待最后公布成绩的课程
公布成绩,每等待一天就会产生C不愉快度。对于第i门课程,按照原本的计划,会在第bi天公布成绩。有如下两种
操作可以调整公布成绩的时间:1.将负责课程X的部分老师调整到课程Y,调整之后公布课程X成绩的时间推迟一天
,公布课程Y成绩的时间提前一天;每次操作产生A不愉快度。2.增加一部分老师负责学科Z,这将导致学科Z的出成
绩时间提前一天;每次操作产生B不愉快度。上面两种操作中的参数X,Y,Z均可任意指定,每种操作均可以执行多次
,每次执行时都可以重新指定参数。现在希望你通过合理的操作,使得最后总的不愉快度之和最小,输出最小的不
愉快度之和即可
Input
第一行三个非负整数A,B,C,描述三种不愉快度,详见【问题描述】;
第二行两个正整数n,m(1≤n,m≤105),分别表示学生的数量和课程的数量;
第三行n个正整数ti,表示每个学生希望的公布成绩的时间;
第四行m个正整数bi,表示按照原本的计划,每门课程公布成绩的时间。
1<=N,M,Ti,Bi<=100000,0<=A,B,C<=100000
Output
输出一行一个整数,表示最小的不愉快度之和。
Sample Input
100 100 2
4 5
5 1 2 3
1 1 2 3 3
4 5
5 1 2 3
1 1 2 3 3
Sample Output
6
由于调整操作产生的不愉快度太大,所以在本例中最好的方案是不进行调整; 全部
5 的门课程中,最慢的在第 3 天出成绩;
同学 1 希望在第 5 天或之前出成绩,所以不会产生不愉快度;
同学 2 希望在第 1 天或之前出成绩,产生的不愉快度为 (3 - 1) * 2 = 4;
同学 3 希望在第 2 天或之前出成绩,产生的不愉快度为 (3 - 2) * 2 = 2;
同学 4 希望在第 3 天或之前出成绩,所以不会产生不愉快度;
不愉快度之和为 4 + 2 = 6 。
由于调整操作产生的不愉快度太大,所以在本例中最好的方案是不进行调整; 全部
5 的门课程中,最慢的在第 3 天出成绩;
同学 1 希望在第 5 天或之前出成绩,所以不会产生不愉快度;
同学 2 希望在第 1 天或之前出成绩,产生的不愉快度为 (3 - 1) * 2 = 4;
同学 3 希望在第 2 天或之前出成绩,产生的不愉快度为 (3 - 2) * 2 = 2;
同学 4 希望在第 3 天或之前出成绩,所以不会产生不愉快度;
不愉快度之和为 4 + 2 = 6 。
HINT
存在几组数据,使得C = 10 ^ 16
Source
考场上还是静不下心来
总感觉这题是个dp
然后直接弃掉了。
其实还是挺简单的。
我们钦定一个试卷被批完的最晚时间
然后通过二分+前缀和计算出学生的不愉快度
再利用二分+后缀和计算出让最后一个被批完的试卷的时间满足要求的不愉快的
两者求和取最小值就可以了
这道题的关键是看出学生不满意度和试卷被批完的时间之间的单调关系
然后要想到枚举时间
#include<cstdio> #include<cmath> #include<algorithm> #define int unsigned long long using namespace std; const int MAXN=1e5+10; const int INF=1e19; inline int read() { char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int A,B,C; int N,M; int t[MAXN],b[MAXN]; int sumt[MAXN],sumb[MAXN];//t的前缀与b的后缀 int limit,ans=INF; main() { #ifdef WIN32 freopen("a.in","r",stdin); #endif A=read();B=read();C=read(); N=read();M=read(); for(int i=1;i<=N;i++) t[i]=read(); for(int i=1;i<=M;i++) b[i]=read(),limit=max(limit,b[i]); sort(t+1,t+N+1); sort(b+1,b+M+1); for(int i=1;i<=N;i++) sumt[i]=t[i]+sumt[i-1]; for(int i=M;i>=1;i--) sumb[i]=b[i]+sumb[i+1]; for(int i=1;i<=limit;i++) { int l=1,r=N,ans1=0,ans2=0,now=0; while(l<=r) { int mid=l+r>>1; if(t[mid]<i) ans1=mid,l=mid+1; else r=mid-1; } l=1,r=M; while(l<=r) { int mid=l+r>>1; if(b[mid]>i) ans2=mid,r=mid-1; else l=mid+1; } if(ans1!=0) now+=(ans1*i-sumt[ans1])*C; if(ans2!=0) { int need=(sumb[ans2]-(M-ans2+1)*i); if(A>=B) now+=need*B; else { int have=(ans2-1)*i-(sumb[1]-sumb[ans2]); if(have>=need) now+=need*A; else now+=have*A+(need-have)*B; } } ans=min(ans,now); } printf("%lld",ans); return 0; }
作者:自为风月马前卒
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。