这三道题分别对应bzoj4868~4870,pdf没法往这放,因此放弃了。
T1:
方法1(正解):三分法
考虑暴力枚举最晚公布的时间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、这样把这两个函数加起来,导数也是递增的,所以它是一个下凸函数(当然也有可能退化成单调函数)。
#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; }
注意到有两个数据C=1016,如果考试现场写的时候怕爆炸,可以特判一下用足够的1、2操作让每个学生都不等待,因为这种情况只要有一个学生等待答案就不是最优。
三分法的时间复杂度……根据 T(n)=T(2n/3)+O(n) 可以知道复杂度大概是O(2n log3 n),比O(n log2 n)(二分)慢了不少,但好歹也是log级别的。
方法2:枚举DDL
直接枚举Deadline(最晚成绩公布时间),时间复杂度O(n)......
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #define MAXN 100005 6 #define INF 1e16 7 typedef long long LL; 8 using namespace std; 9 int n,m,a,b,c; 10 LL student[MAXN],course[MAXN]; 11 int read() 12 { 13 int x=0;char c=getchar(); 14 while(c<‘0‘||c>‘9‘)c=getchar(); 15 while(c>=‘0‘&&c<=‘9‘)x=x*10+c-‘0‘,c=getchar(); 16 return x; 17 } 18 int main() 19 { 20 a=read(),b=read(),c=read(),n=read(),m=read(); 21 int day=0;LL cost=0,mov=0,left=0,ans; 22 for(int i=1;i<=n;i++) 23 {int t;t=read();student[t]++;day=max(day,t);} 24 for(int i=1;i<=m;i++) 25 {int t;t=read();course[t]++;day=max(day,t);} 26 for(int i=1;i<=day;i++) 27 { 28 cost+=student[i]*(day-i); 29 left+=course[i]*(day-i); 30 student[i]+=student[i-1];//维护前缀和 31 course[i]+=course[i-1]; 32 } 33 ans=cost*c; 34 for(int i=day-1;i>0;i--) 35 { 36 mov+=(m-course[i]);//在这天后完结的课需要向前移动一天 37 left-=course[i];//前面的所有课程向后移动的范围减少了1天 38 cost-=student[i];//减掉希望在这天以前完结的学生一天的不满意度 39 if(c>=INF&&cost)continue; 40 LL p=left>0?left:0; 41 if(mov<p)p=mov; 42 if(a<b)ans=min(ans,p*a+(mov-p)*b+cost*c); 43 else ans=min(ans,mov*b+cost*c); 44 } 45 printf("%lld\n",ans); 46 return 0; 47 }
T2:
数学题ToT,和bzoj3884的坑爹程度有些相似。