[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);
}

.

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

bzoj4570【SCOI2016】妖怪

凸包

bzoj4570 [Scoi2016]妖怪(凸包+对勾函数最值)

bzoj4570 [Scoi2016]妖怪原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4570题意: 邱老师有n只妖怪,每只妖怪有攻击力...
  • Bfk_zr
  • Bfk_zr
  • 2017年12月10日 00:31
  • 13

BZOJ4570 SCOI2016 妖怪 凸包+双钩函数求最值

题目大意现在有NN只妖怪,每个妖怪都有两个权值Ai,BIA_i,B_I,现在对于每种环境,设置两个参数(a,b)(a,b)表示我可以把一直怪兽的BiB_i减去kbkb,AiA_i加上kaka(kk可以...

BZOJ4570: [Scoi2016]妖怪

题目传送门4570: [Scoi2016]妖怪Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 491 Solved: 125[Submit][Statu...
  • glk__
  • glk__
  • 2016年08月24日 13:14
  • 141

【bzoj4571: [Scoi2016]美味】区间异或和最大 ,可持久化线段树(主席树)

4571: [Scoi2016]美味 Time Limit: 30 Sec  Memory Limit: 256 MB Submit: 704  Solved: 380 [Submit][Sta...

[SCOI2016][JZOJ4640]妖怪

题目大意给定nn个二元组(xi,yi)(x_i,y_i),求一个能使max{xi+yi+baxi+abyi|i∈[1,n]}max\{x_i+y_i+{b\over a}x_i+{a\over b}y...

[BZOJ4571][SCOI2016]美味(二分+主席树)

可以利用二分的思想,具体步骤,就是从高到低确定每一个二进制位的数值。 可以看出,如果当前已经确定了前几位,那么这个解的范围一定是一个连续的区间。举个例子,假设初始解的枚举范围是[0,262143][...

[BZOJ]1090: [SCOI2003]字符串折叠 区间DP

Description 折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S  S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S)  SSSS…S(X个S)。 3....

BZOJ 1090 SCOI 2003 字符串折叠 区间DP

题目大意:给出一个字符串,在不改变这个字符串的内容的情况下可以将它进行折叠,具体见题里说的吧。问这个字符串最短可以折叠成多长。 思路:数据范围才100,怎么暴力怎么搞。首先是一个区间DP,设f[i...

【BZOJ 1068】【SCOI 2007】压缩 【区间DP】

题目跳转: http://www.lydsy.com/JudgeOnline/problem.php?id=1068Description  给一个由小写字母组成的字符串,我们可以用一种简单的方法来压...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[bzoj4570][scoi2016]妖怪 二分区间
举报原因:
原因补充:

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