【BZOJ 4868】【2017六省联考】期末考试

来源:互联网 发布:js图片循环滚动代码 编辑:程序博客网 时间:2024/06/05 00:13

考虑暴力枚举最晚公布的时间x,关注到2操作是没有负面影响的1操作,所以如果A大于B,那么只需用2操作就可以了,否则先用1操作,不能用1操作后再用2操作。这样就能把b数组全部变成小于等于x,在加上额外的不愉快度就可以了。这个算法的时间复杂度是O(N2),可以拿60分。
如果你去打表就能发现不愉快度关于时间是一个下凸函数,可以用三分做。具体的证明是这样的:
1、修改代价关于时间是单调递减的,也就是说越晚出成绩修改代价越小;
2、额外代价关于时间是单调递增的,也就是说越晚出成绩额外代价越大;
3、最重要的一句,这两个函数的导数都是递增的。
为什么呢?感性的想一下,拿额外代价举例子,比如五个人的时间分别是1、2、2、3、3,时间从1改到2,需要额外代价的1个人(第一个人),但是从2改到3,需要额外代价的就变成了3个人,从3改到4所有的人都需要额外代价,也就是说刚开始额外代价增加慢,到后来额外代价增加快。
4、这样把这两个函数加起来,导数也是递增的,所以它是一个下凸函数(当然也有可能退化成单调函数)。

我写的就是三分。不过听说还能二分(二分导数?)正解中还有一种做法是用DDL做,就可以做到线性orz
这道题最恶心的是中间有一组C=1e16,被坑死了,直接特判掉即可。注意那个等号,因为两个都是inf,答案要更小。

#include<cmath>#include<cstdio>#include<vector>#include<queue>#include<cstring>#include<iomanip>#include<stdlib.h>#include<iostream>#include<algorithm>#define ll long long#define mod 1000000007#define N 100005#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;ll A,B,C,l,r,res,res1,r1,r2,inf;int n,m,i,j,mid1,mid2;int t[N],b[N];ll calc(int x){    int i; ll sum = 0,need = 0;    fo(i,1,m) if (b[i] <= x) sum += x - b[i];    fo(i,1,m) if (b[i] > x) need += b[i] - x;    if (A < B)        {            if (need <= sum) return (ll)A*need;            return (ll)(A*sum+B*(need-sum));        }    return (ll)B*need;}int main(){    inf = 1; fo(i,1,62) inf *= 2;    scanf("%lld%lld%lld",&A,&B,&C);    scanf("%d%d",&n,&m);    fo(i,1,n) scanf("%d",&t[i]);    l = inf; r = -inf;    fo(i,1,n) r = max(r,(ll)t[i]) , l = min(l,(ll)t[i]);    fo(i,1,m) scanf("%d",&b[i]);    while (r - l > 5)        {            mid1 = l + (r - l) / 3;            mid2 = mid1 + (r - l) / 3;            r1 = calc(mid1); r2 = calc(mid2);            fo(j,1,n) if (t[j] < mid1)                 if (C == 1e16) {r1 = inf; break;} else r1 += C * (mid1 - t[j]);            fo(j,1,n) if (t[j] < mid2)                 if (C == 1e16) {r2 = inf; break;} else r2 += C * (mid2 - t[j]);            if (r1 <= r2) r = mid2; else l = mid1;        }    res = -1;    fo(i,l,r)        {            res1 = calc(i);            fo(j,1,n) if (t[j] < i)                 if (C == 1e16) {res1 = inf; break;} else res1 += C * (i - t[j]);            if (res == -1) res = res1;            res = min(res,res1);        }    printf("%lld\n",res);    return 0;}
0 0