[bzoj4570][scoi2016]妖怪 二分区间

原创 2016年05月30日 22:14:38

写在前面的话:
本题正解是凸包而不是二分,科学做法请右转各神犇blog。以下是蒟蒻的二分做法。
我的代码在不开o2的情况下大约800ms-900ms一个点 开o2大约300ms-400ms一点。

首先设k=ab,记xi=atki,yi=dnfi
则对于每个1in,战斗力为fk(i)=(1k+1)xi+(k+1)yi
发现这个是对勾函数,我们可以利用这个做很多事情。(就是卡常辣)

然后我们要二分的是k 不是二分答案!

然后我们要二分的是k 不是二分答案!

然后我们要二分的是k 不是二分答案!


虽然看上去解并不随k单调或者有唯一极值,也找不到二分的方向,但是我们可以采取这样的策略:
1.二分时记录当前最优ans ,上次枚举的last_k和妖怪last_i,当我们枚举到一个k0时,记录当前得到最大的f(i)的妖怪为maxi。
2.如果答案比之前更优,那么直接更新last_ans,last_k和last_i,下一次二分往maxi的那条对勾函数更低的方向走。往反方向走答案肯定不会更优。(ps:这时已经知道最后答案肯定在 f(maxi)和 maxi对勾函数最低点 之间,或许可以区间-答案交替二分?好像很炫酷)
3.如果答案并不比之前优,下一次二分往last_k的方向走。注意此时什么都不更新。
4.注意实现时用last_dir代替last_k和last_i记录,因为last_k只用到它的方向。还有注意下图this_k就相当于当前二分l和r的mid

2的正确性显然,3的正确性…并不是那么显然。你可以脑补出这样一幅图:

根据3 在this_k时答案没有更优,往右走,就会错过了左边更优的答案?
事实上上图情况并不会发生,因为对勾函数(在第一象限)是单峰的,并且严格先递减后递增
图大概是这样,由对勾函数性质可以推出虚线部分,就不写数学证明了。
这里写图片描述
当last_k在this_k左边时同理,当maxi=last_i时显然。

顺便一提还有一个小优化:因为题中对勾函数最低点可以O(1)算,最开始二分时就不用l=eps,r=inf了,可以l=所有对勾函数最左边最低点,r=所有对勾函数最右边最低点。(我语文弱啊还是看代码吧)

#include <cstdio>
#include <cmath>
//#include <ctime>
#define N 1000005
#define INF (10000000000000ll)
#define eps 1e-8
#define Min(a,b) (a<b?a:b)
#define Max(a,b) (a>b?a:b)
inline int RD()
{
    static int res;
    static char cr;
    while( (cr=getchar())<'0' || cr>'9');
    res=cr-'0';
    while( (cr=getchar())>='0' && cr<='9')
        res=(res<<1)+(res<<3)+cr-'0';
    return res;
}
int x[N],y[N],n;
double ans;
int maxi;
double check(double k)
{
    double tox=1/k+1,toy=k+1;
    double res=0,r;
    for(int i=1;i<=n;i++)
    {
        r=tox*x[i]+toy*y[i];
//      if(r>ans) return -1;//实测这个没用..
        if(r>res)
            maxi=i,res=r;
    }
    return res;
}
int  main()
{
//  int c1=clock();
//  freopen("monster.in","r",stdin);
    n=RD();
    double l=INF,r=eps,temp;
    for(int i=1;i<=n;i++)
    {
        x[i]=RD();y[i]=RD();
        temp=sqrt((double)x[i]/y[i]);
        l=Min(l,temp);
        r=Max(r,temp);
    }
    double mid,res; ans=INF;
    int last_dir=-1;
    int lim=42;//保证正确性的情况下,卡时//=42000000/n;
    //二分42次是拍出来的.什么泥问我拿什么对拍?lim=100就好了
    while(lim--)
    {
        mid=(l+r)/2;
        res=check(mid);
        if(res>ans)
            last_dir? l=mid: r=mid;
        else//res<ans
        {
            ans=res;
            temp=sqrt((double)x[maxi]/y[maxi]);
            mid<temp? l=mid: r=mid;
            last_dir=(mid>temp);
        }
    }
    printf("%.04lf",ans);   
//  printf("\ntime %d",clock()-c1);
}

.

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

hiho 1561 观光旅行 [set启发式合并+想法]

hiho 1561 观光旅行 [set启发式合并+想法]

BZOJ 3123 主席树 启发式合并

思路: 主席树 搞树上的k大 x+y-lca(x,y)-fa(lca(x,y)) 按照size小树往大树上插 启发式合并 n*log^2n的 搞定~//By SiriusRen #inclu...

【题解】codeforces741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

题目链接 题意:给定一棵有根树,结点编号为1~n,根结点为结点1。每条边上有一个字母,求各子树内最长的满足“路径上的字母经过重排后可以构成回文串”的简单路径。 分析:对结点u,用2进制数vec[u...

【算法】树上启发式合并算法

树上启发式合并算法是启发式合并算法在树上的应用。下面我直接通过一个例子来讲解这个算法。         例:给定一棵有根树,树的结点编号为1~n,根结点为结点1。结点i有颜色col[i],其中1≤co...

怎样更有力气

题目描述 OI大师抖儿在夺得银牌之后,顺利保送pku。这一天,抖儿问长者:“我虽然已经保送了,但我的志向是为国家健康工作五十年。请问我应该怎样变得更有力气?”   长者回答:“你啊,Too Y...

高斯消元的一些模板 整理自他人,感谢!

//高斯消元法解异或方程组,返回方程解得个数。 const int N = 505; int A[N][N];//关系矩阵 ll Gauss(int equ,int var){//返回解得...

【XSY1081】随机存储器 网络流

题目描述  Bob有2n2^n字节的内存,编号为[0,2n−1)[0,2^n-1)。他想对每个字节的内存分别分配一个值。对于编号为ii的内存,如果它被分配了一个值j(0≤j<2m)j(0\leq j<...

1212全民疯抢 冰贝韩版修身条纹装饰假两件长款棉衣加厚外套BK927

促销价格 : 470.0 元 掌柜 : 冰贝服饰旗舰店 信用 : 30天累计售出 : 件 宝贝与描述相符 : 4.6低于1.15% 卖家的服务态度 : 4.7持平-------- 卖家发...

关于treap启发式合并的一点脑洞(以bzoj2809为例)

首先我直到bzoj2809正解应该是可并堆,之所以写treap启发式合并单纯只是因为这个脑洞… 首先我们有两个treap,分别是 A 和 B ,它们的节点数分别为 n 和 m (n<m)(n<m)。网...

hdu 4680 splay,启发式合并

hdu 4680 splay,启发式合并 这个题是按照权值建树,一般的区间树操作可能得修改一下。 几个操作也很有想法: 1.合并两个集合。这里用到启发式合并的方式,也就是把小的集合一个个插到...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)