这三道题分别对应bzoj4868~4870,pdf没法往这放,因此放弃了。
T1:
方法1(正解):三分法
考虑暴力枚举最晚公布的时间x,关注到2操作是没有负面影响的1操作,所以如果A大于B,那么只需用2操作就可以了,否则先用1操作,不能用1操作后再用2操作。这样就能把b数组全部变成小于等于x,在加上额外的不愉快度就可以了。这个算法的时间复杂度是,可以拿60分。
如果你去打表就能发现不愉快度关于时间是一个下凸函数,可以用三分做。具体的证明是这样的:
1、修改代价关于时间是单调递减的,也就是说越晚出成绩修改代价越小;
2、额外代价关于时间是单调递增的,也就是说越晚出成绩额外代价越大;
3、最重要的一句,这两个函数的导数都是递增的。
感性的想一下,拿额外代价举例子,比如五个人的时间分别是1、2、2、3、3,时间从1改到2,需要额外代价的1个人(第一个人),但是从2改到3,需要额外代价的就变成了3个人,从3改到4所有的人都需要额外代价,也就是说刚开始额外代价增加慢,到后来额外代价增加快。
4、这样把这两个函数加起来,导数也是递增的,所以它是一个下凸函数(当然也有可能退化成单调函数)。
#include<cstdio> #include<iostream> #include<algorithm> #define MN 100000 #define ll long long using namespace std; inline int read() { ll x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x; } int n,m,t[MN+5],s[MN+5]; ll A,B,C,ans=1e18; ll calc(int tms) { ll sum=0,left=0,need=0; for(int i=1;i<=n;++i) sum+=C*max(0,tms-t[i]); for(int i=1;i<=m;++i) if(s[i]>tms) need+=s[i]-tms; else left+=tms-s[i]; if(B<A)return sum+need*B; else if(left>=need) return sum+need*A; else return sum+left*A+(need-left)*B; } void Solve(int l,int r) { if(r-l<=1) { for(int i=l;i<=r;++i) ans=min(ans,calc(i)); return; } int m1=(r-l+1)/3+l,m2=(r-l+1)/3*2+l; if(calc(m1)<calc(m2)) Solve(l,m2-1); else Solve(m1+1,r); } int main() { A=read();B=read();C=read(); n=read();m=read();int mx=0; for(int i=1;i<=n;++i) mx=max(mx,(t[i]=read())); for(int i=1;i<=m;++i) mx=max(mx,(s[i]=read())); Solve(1,mx); printf("%lld\n",ans); return 0; }