HEOI2015小L的白日梦

题面链接

洛咕

sol

为什么网上面只有神仙题解啊!!!

引起我这种蒟蒻不适QAQ。

性质证明留给巨佬

然后我只贴性质了QwQ。

1.一定存在最优解每一天不高兴的概率是单调不增的。

2.一定存在最优解它选取的项目是所有项目按照不高兴的概率排序后的前缀一段加上后缀一段。

3.每一次选取的项目种类只有三种可能的情况:选了1个,全部选完,其他。且处于第三种状态的至多一个。

认认真真写的代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gt getchar()
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
inline int in()
{
    int k=0;char ch=gt;
    while(ch<'-')ch=gt;
    while(ch>'-')k=k*10+ch-'0',ch=gt;
    return k;
}
struct node
{
    long double val;
    int cnt;
    node(){}
    node(long double _val,int _cnt):val(_val),cnt(_cnt){}
    inline void read()
        {
            int a,b;
            scanf("%d/%d",&a,&b);
            val=(long double)a/b;
            scanf("%d",&cnt);
        }
    inline bool operator<(const node &a)const{return val>a.val;}
}A[150005],B[350005];
int n,m;
long double calc()
{
//注意前缀后缀两次的意义是不一样的!
    long double ret=1e18,sum=0;
    ll now=1,res=m;
    for(int i=n;i;--i)
        sum+=(B[i].cnt-1)*B[i].val*(1-B[i].val)+(1-B[i].val)*B[i+1].val,res-=B[i].cnt;
//我们可以枚举前缀的位置,后缀端点是单调的,这里强制了前缀末端只剩一,其它在后缀
    for(int i=1;i<=n;++i)
    {
        res-=B[i].cnt;
        while(now<=n&&res<=0)
            sum-=(B[now].cnt-1)*B[now].val*(1-B[now].val)+(1-B[now].val)*B[now+1].val,res+=B[now++].cnt;
        if(res<=0)break;
        sum+=(B[i].cnt-1)*B[i].val*(1-B[i].val)+
            (1-B[i-1].val)*B[i].val;
        ret=std::min(ret,sum+
                     (res-1)*B[now-1].val*(1-B[now-1].val)+
                     (1-B[now-1].val)*B[now].val+
                     (1-B[i].val)*B[now-1].val);
    }
    res=m,sum=0;
//求只有前缀的方案
    for(int i=1;i<=n;++i)
    {
        int mn=std::min(res,(ll)B[i].cnt);
        if(!mn)break;
        else res-=mn,sum+=(mn-1)*B[i].val*(1-B[i].val)+(1-B[i-1].val)*B[i].val;
    }
    return std::min(ret,sum);
}
int main()
{
    int t=in();
    while(t--)
    {
        n=in(),m=in();int tot=0;
        for(int i=1;i<=n;++i)
        {
            A[i].read();
            if(!A[i].cnt)--i,--n;
        }
        std::sort(A+1,A+n+1);
        for(int i=1;i<=n;++i)
        {
            B[++tot]=node(A[i].val,1);
            if(--A[i].cnt)
            {
                if(A[i].cnt>1)
                    B[++tot]=node(A[i].val,A[i].cnt-1);
                B[++tot]=node(A[i].val,1);
            }
        }
        B[0].val=1,B[(n=tot)+1].val=0;
        long double ans=calc();
//B[i].val=1-B[i].val是说你把前缀后缀倒过来做
//本来应该是 (1-a0)a1+(1-a1)a2...(1-an-1)an
//然后变成了 an(1-an-1)...a2(1-a1)+a1(1-a0)
//所以要变成 1-B[i].val
        for(int i=1;i<=n;++i)B[i].val=1-B[i].val;
        std::reverse(B+1,B+n+1);
        ans=std::min(ans,calc());
        printf("%.6lf\n",(double)fabs(ans));
    }
    return 0;
}

转载于:https://www.cnblogs.com/cx233666/p/9853257.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值