bzoj4030【HEOI2015】小L的白日梦

题意:http://www.lydsy.com/JudgeOnline/problem.php?id=4030

sol  :orz Yousiki http://www.cnblogs.com/yousiki/p/6490769.html

   orz jiry http://jiruyi910387714.is-programmer.com/posts/90425.html

   有三个结论:

     1、一定存在一个最优解,使得每天不高兴的概率单调递增

        这个结论很显然,考虑对于一组逆序对将其交换,答案更优

     2、一定存在最优解,由排序后的一段前缀+一段后缀构成

        如果把前缀一个点放到中间比原来优,那么把这个点放到后面一定更优(或者一样)

     3、每一种选取的项目有三种可能:选一个,全选,其他,且第三种至多一个

        这个不会证QAQ,Yousiki大佬说随便想想就知道了QAQ

   这样的话就可以假设第三种情况在后缀(前后缀分别做一次),

   线性往后扫前缀,每次向后跳一个块,考虑后缀的期望会怎么更新,再处理一个快+1的情况即可

   可以把一个点拆成三个:1,cnt,1,这样写起了方便一些,不用再特判

   代码莫名其妙数据一大就WA.......完全不知道为什么QAQ,弃疗了,cogs最后一个点还莫名其妙过了....50points

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define inf 1e18
#define double long double
#define int long long
using namespace std;
const int Mx=100010;
struct Node { int c; double p,w; } A[3*Mx],tmp[Mx];
bool cmp(Node a,Node b) { return a.p>b.p; }
int n,k,tot,now,sigma;
double ans,sum;
void solve()
{
    sum=0,now=1,sigma=k;
    for(int i=n;i>=1;i--) sum+=(A[i].c-1)*A[i].w+(1-A[i].p)*A[i+1].p,sigma-=A[i].c;
    for(int i=1;i<=n;i++)
    {
        sigma-=A[i].c;
        while(now<=n&&sigma<=0) sum-=(A[now].c-1)*A[now].w+(1-A[now].p)*A[now+1].p,sigma+=A[now++].c;
        if(sigma<=0) break; sum+=(A[i].c-1)*A[i].w+(1-A[i-1].p)*A[i].p;
        ans=min(ans,sum+(sigma-1)*A[now-1].w+(1-A[now-1].p)*A[now].p+(1-A[i].p)*A[now-1].p);
    }
    sigma=k,sum=0;//初始状态
    for(int i=1,mn=min(sigma,A[i].c);i<=n&&mn;i++)
        sigma-=mn,sum+=(mn-1)*A[i].w+(1-A[i-1].p)*A[i].p;
    ans=min(ans,sum);
}
signed main()
{
    int T; scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&k);
        for(int i=1,x,y;i<=n;i++)
            scanf("%lld/%lld%lld",&x,&y,&tmp[i].c),tmp[i].p=(double) x/y,tmp[i].w=(1-tmp[i].p)*tmp[i].p;
        sort(tmp+1,tmp+1+n,cmp); tot=0;
        for(int i=1;i<=n;i++)//拆点
        {
            if(tmp[i].c==1) A[++tot]=tmp[i];
            if(tmp[i].c>1) A[++tot]=tmp[i],A[tot].c=1,A[++tot]=tmp[i],A[tot].c--;
            if(tmp[i].c>2) A[tot].c--,A[++tot]=tmp[i],A[tot].c=1;
        } n=tot;
        ans=inf,A[0].p=1,A[n+1].p=0;
        solve();
        for(int i=1;i<=(n+1)/2;i++) A[i].p=1-A[i].p,A[n-i+1].p=1-A[n-i+1].p,swap(A[i],A[n-i+1]);
        solve();
        printf("%.6LF\n",fabs(ans));
    }
}

 

转载于:https://www.cnblogs.com/xiaoxubi/p/6523985.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值