题面
题解
因为每个学生产生的代价其实只跟自身属性和最后一门成绩的公布时间相关,
所以考虑如果我们固定一个时间t作为最后一场,那么我们就可以快速算出此时的代价了。 首先在t之前的成绩都可以用来和在t后面的成绩多次配对进行第一种操作。 因此我们先贪心的进行第一种操作,能搞几次就搞几次,如果剩下还有在t后面的成绩,即再用第二种操作强行提前(如果第二种操作的代价比第一种操作低,那就将第一种操作的代价设为第二种操作的代价就可以保证结果正确性了) 而要算出这样决策的代价,我们只需要知道在t前面的成绩可以供给几次操作1,在t后面的操作需要提前几次即可。 这2个东西都可以直接算,前者就是把当前时间 - 在t前面的成绩公布时间做一次求和,所以我们只需要对2部分分别求和(过程中前缀和优化),即可做到全局线性的复杂度(线性来自于2次前缀和) 后者也可以用一样的方法求。 然后就是直接算了。 复杂度就是\(O(t + n)\),,,,非常优秀#includeusing namespace std;#define R register int#define LL long long//大概是中间过程会爆?因为如果爆成了负数,虽然不应该成为答案,但是取min就会取这个负数了#define us unsigned#define AC 101000#define inf 1000000000000000000LLint n, m, l, r;//tot表示总的公布时间,sum表示无需减小的公布时间,all表示需要等待的学生的sum(s[i])us LL s[AC], t[AC], A, B, C, ans = inf, tot, all, sum;inline int read(){ int x = 0;char c = getchar(); while(c > '9' || c < '0') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x;}inline void upmin(us LL &a, us LL b) {if(b < a) a = b;}void pre(){ A = read(), B = read(), C = read(); n = read(), m = read(); for(R i = 1; i <= n; i ++) s[i] = read(); for(R i = 1; i <= m; i ++) t[i] = read(), tot += t[i]; sort(s + 1, s + n + 1), sort(t + 1, t + m + 1);}void work(){ int l = 0, r = 0; if(B < A) A = B;//如果直接提前比交换代价还要小的话,不如全都直接提前 for(us LL i = 1; i <= t[m]; i ++) { while(l < n && s[l + 1] <= i) ++ l, all += s[l]; while(r < m && t[r + 1] <= i) ++ r, sum += t[r]; LL rnt = C * (l * i - all);//获取学生等待带来的贡献 LL rest = i * r - sum;//获取前面的公布时间最多分几天向后拖延 LL have = tot - sum - i * (m - r);//获取后面还需要提前几天 if(have) { if(rest >= have) rnt += have * A;//如果rest足够就直接全部交换(如果提前代价比较小就会直接提前) else rnt += rest * A + (have - rest) * B;//否则就能交换就交换,不能交换就提前 } upmin(ans, rnt); } printf("%lld\n", ans);}int main(){// freopen("in.in", "r", stdin); pre(); work();// fclose(stdin); return 0;}