BZOJ4868: [Shoi2017]期末考试

BZOJ4868

我的做法好像有点奇葩。。用的二分。
说一下自己的思路。
先将数组 t b排序,记 mn=t1 , mx=bm
分别表示最早的想知道成绩的时间,和最晚的发布成绩的时间。
那么调整发布成绩的最晚时间只会在 mn mx 之间。
如果 mx<=mn 直接输出0.
假如确定了发布成绩的最晚时间 t
那么Ans=t+tC
这两部分都可以在 logn 时间内求出。
具体求法:
先找到 b 数组中大于等于t的第一个位子 pos ,令 ss=pos1i=1tbi s=mi=posbit 。这两部分记了数组 b 的前缀和都可以O(1)求了。如果 B<=A ,那么一定用 B 更优,因为A还会使某些值变大,这时第一部分答案 Ans1=Bs
另外情况下,如果 ss>=s 那么全部都可以用 A 不会使前面小的值大于t,此时 Ans1=As 。如果 ss<s ,那么能用 A 的用A,剩下的用 B ,此时Ans1=Ass+B(sss)
第二部分就好求了。二分找到 t 中大于等于t的第一个位置。。记录个前缀和就可求了。。

这个方法无法过掉全部数据。( C 只有三个取值范围<=1e2, <=1e5 <script type="math/tex" id="MathJax-Element-15883"><=1e5</script>, 1e16 )当 C=1e16 时,第二部分答案会爆掉 long long
发现当 C=1e16 时, A,B<=1e5 ,那这时显然 t=mn 时答案不会超过 1e15 。特判一下就可以了。

【代码】

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <complex>
#include <algorithm>
#include <cmath>
#define N 100005
#define INF 0x7fffffff
#define mod 1000000007
using namespace std;
const double pi=acos(-1);
typedef long long ll;
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int A,B,n,m,mn=N,mx=0,id;
int t[N],b[N];
ll sum[N],Sum[N],ans=1e18,C;

int Find(int Del)
{
    int l=1,r=m,rtn=1;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(b[mid]>=Del) rtn=mid,r=mid-1;
        else l=mid+1;
    }
    return rtn;
}

int Findf(int Del)
{
    int l=1,r=n,rtn=1;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(t[mid]<=Del) rtn=mid,l=mid+1;
        else r=mid-1;
    }
    return rtn;
}

void Deal(int Mxb)
{
    int pos=Find(Mxb);ll tmp=0,Tmp=0;
    ll s=Sum[m]-Sum[pos-1]-1LL*(m-pos+1)*Mxb;
    ll ss=1LL*(pos-1)*Mxb-Sum[pos-1];
    if(B<=A) tmp=1LL*B*s;
    else if(ss>=s) tmp=1LL*s*A;
    else tmp=1LL*ss*A+1LL*(s-ss)*B;
    pos=Findf(Mxb);
    Tmp=1LL*(1LL*Mxb*(pos)-sum[pos])*C;
    tmp+=Tmp;
    ans=min(ans,tmp);
}

int main() 
{
    A=read(),B=read(),C=read();
    n=read(),m=read();
    for(int i=1;i<=n;i++) t[i]=read();
    for(int i=1;i<=m;i++) b[i]=read();
    sort(t+1,t+1+n);sort(b+1,b+1+m);
    mn=t[1];mx=b[m];
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+t[i];
    for(int i=1;i<=m;i++) Sum[i]=Sum[i-1]+b[i];
    if(mx<=mn) 
    {
        printf("0\n");
        return 0;
    }
    if(C==1e16)
    {
        Deal(mn);
        printf("%lld\n",ans);
        return 0;
    }
    for(int i=mx;i>=mn;i--)
        Deal(i);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值