zoukankan      html  css  js  c++  java
  • 【2018.4.5】Shoi2017题集

    这三道题分别对应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
    ",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
    ",ans);
    46     return 0;
    47 }
    T1-2

    T2:

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

  • 相关阅读:
    hdu1875(最小生成树prime)
    hdu1839(最小生成树)
    poj2739(尺取法+质数筛)
    poj2100(尺取法)
    codeforces-div2-449-B
    gym-101350M
    gym-10135I
    gym-101350H
    gym-101350D
    hdu 5569
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/8722942.html
Copyright © 2011-2022 走看看