码迷,mamicode.com
首页 > 其他好文 > 详细

【2018.4.5】Shoi2017题集

时间:2018-04-05 16:26:23      阅读:207      评论:0      收藏:0      [点我收藏+]

标签:时间复杂度   algorithm   def   show   int   lse   pac   复杂   course   

这三道题分别对应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;
}
T1-1

注意到有两个数据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 }
T1-2

 

T2:

数学题ToT,和bzoj3884的坑爹程度有些相似。

 

【2018.4.5】Shoi2017题集

标签:时间复杂度   algorithm   def   show   int   lse   pac   复杂   course   

原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/8722942.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!