bzoj 2402: 陶陶的难题II 二分答案维护凸包

2402: 陶陶的难题II

Time Limit: 40 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 68  Solved: 45
[Submit][Status]

Description

Input

第一行包含一个正整数N,表示树中结点的个数。
第二行包含N个正实数,第i个数表示xi (1<=xi<=10^5)。
第三行包含N个正实数,第i个数表示yi (1<=yi<=10^5)。
第四行包含N个正实数,第i个数表示pi (1<=pi<=10^5)。
第五行包含N个正实数,第i个数表示qi (1<=qi<=10^5)。
下面有N-1行,每行包含两个正整数a,b(1<=a,b<=N),表示树中的边。
第N+5行包含一个正整数M,表示询问的个数。
最后M行,每行包含正整数a,b(1<=a,b<=N),表示一次询问。

Output

共M行,每行一个实数,第i行的数表示第i次询问的答案。
只要你的输出和我们的输出相差不超过0.001即为正确。

Sample Input

5
3.0 1.0 2.0 5.0 4.0
5.0 2.0 4.0 3.0 1.0
1.0 3.0 2.0 4.0 5.0
3.0 4.0 2.0 1.0 4.0
1 2
1 3
2 4
2 5
4
2 3
4 5
2 4
3 5

Sample Output

2.5000
1.5000
1.5000
2.5000

HINT

100%的数据满足N,M≤ 30,000。

1<=Xi,Yi,Pi,Qi<=10^8

  注意题目中有这么一句话:“只要你的输出和我们的输出相差不超过0.001即为正确。”,这句话可以算作是二分答案的标志,由于1.4999...和1.5000...无论如何精确,四舍五入后都不同,所以这样的二分题只会用spj,反过来,也能证明这道题一定要用二分。

设原式x==p1
  y==v1
  p==p2
  q==v2

二分答案ans
  (v1+v2)/(p1+p2)>=ans
  (v1+v2)>=ans*p1+ans*p2
  (v1-ans*p1) + (v2-ans*p2)>=0
对于二分的ans我们只需要分别处理前半部分和后半部分的最大值即可。
  v1-ans*p1==c
  -p1*ans+v1==c
原式 -x*ans+y==c

  可看做对于区间内每一个(x,y),存在一条斜率k=-x,截距b=y的直线,套斜率优化的方法,预处理维护凸包链剖线段树维护即可。

  时间复杂度O(Qlog^3(n)),常数较小

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 31000
#define MAXV MAXN
#define MAXE MAXV*2
#define MAXT MAXN*4
#define lch (now<<1)
#define rch (now<<1^1)
#define smid ((sgt[now].l+sgt[now].r)>>1)
#define maxval 1e8
#define eps 1e-5
#define inf 1e100
typedef double real;
int n;
real v1[MAXN],v2[MAXN],v3[MAXN],v4[MAXN];
int dfsq[MAXN];
struct sgt_node
{
        int l,r,lev;
        int tot0,tot1;
}sgt[MAXT];
struct line
{
        real k,b;
        line(real k,real b):k(k),b(b){}
        line(){};
        real get_h(real x)
        {
                return k*x+b;
        }
        void print()
        {
                printf("Line: k=%.2lf b=%.2lf\n",k,b);
        }
}sgc[2][20][MAXN];
struct point
{
        real x,y;
        point(real x,real y):x(x),y(y){};
        point(){}
        void print()
        {
                printf("Point: (%.2lf,%.2lf)\n",x,y);
        }
}sgp[2][20][MAXN];
bool cmp_point_x(const point &p1,const point &p2)
{
        return p1.x<p2.x;
}
point crossover(line l1,line l2)
{
        point res;
        res.x=(l1.b-l2.b)/(l2.k-l1.k);
        res.y=res.x*l1.k+l1.b;
        return res;
}
pair<real,real> sgt_getv(int now,real ps)
{
        pair<real,real> res;
        int x;
        x=lower_bound(&sgp[0][sgt[now].lev][sgt[now].l+1],&sgp[0][sgt[now].lev][sgt[now].l+sgt[now].tot0],point(ps,-inf),cmp_point_x)
                -sgp[0][sgt[now].lev]-1;
        res.first=sgc[0][sgt[now].lev][x].get_h(ps);
        x=lower_bound(&sgp[1][sgt[now].lev][sgt[now].l+1],&sgp[1][sgt[now].lev][sgt[now].l+sgt[now].tot1],point(ps,-inf),cmp_point_x)
                -sgp[1][sgt[now].lev]-1;
        res.second=sgc[1][sgt[now].lev][x].get_h(ps);
        return res;
}
void Combine(line* res,point* pres,int& totr,line* h1,const int& tot1,line *h2,const int& tot2)
{
        int t1,t2;
        line lnow;
        t1=t2=0;
        while (t1!=tot1 || t2!=tot2)
        {
                if (t1<tot1 && t2<tot2)
                {
                        if (h1[t1].k<h2[t2].k)
                        {
                                lnow=h1[t1++];
                        }else
                        {
                                lnow=h2[t2++];
                        }
                }else if (t1==tot1)
                {
                        lnow=h2[t2++];
                }else if (t2==tot2)
                {
                        lnow=h1[t1++];
                }
                if (totr<=1)
                {
                        res[totr]=lnow;
                        if (totr)
                                pres[totr]=crossover(res[totr-1],res[totr]);
                        totr++;
                }else
                {
                        while (totr>1 && crossover(res[totr-2],lnow).y>=pres[totr-1].y)totr--;
                        res[totr]=lnow;
                        pres[totr]=crossover(res[totr-1],res[totr]);
                        totr++;
                }
        }
}
void Build_sgt(int now,int l,int r,int lev)
{
        sgt[now].l=l,sgt[now].r=r;
        sgt[now].lev=lev;
        if (l==r)
        {
                sgc[0][lev][l]=line(-v2[dfsq[l]],v1[dfsq[l]]);
                sgc[1][lev][l]=line(-v4[dfsq[l]],v3[dfsq[l]]);
                sgt[now].tot0=sgt[now].tot1=1;
            //    printf("At position%d:\n",l);
            //    printf("L1:");sgc[0][lev][l].print();
                //printf("L2:");sgc[1][lev][l].print();
                return ;
        }
        Build_sgt(lch,l,smid,lev+1);
        Build_sgt(rch,smid+1,r,lev+1);
        Combine(&sgc[0][lev][l],&sgp[0][lev][l],sgt[now].tot0,&sgc[0][lev+1][l],sgt[lch].tot0,&sgc[0][lev+1][smid+1],sgt[rch].tot0);
        Combine(&sgc[1][lev][l],&sgp[1][lev][l],sgt[now].tot1,&sgc[1][lev+1][l],sgt[lch].tot1,&sgc[1][lev+1][smid+1],sgt[rch].tot1);
    /*    printf("At position[%d,%d]:\n",l,r);
        for (int i=l;i<l+sgt[now].tot0;i++)
        {
                sgc[0][lev][i].print();
                if (i!=l)
                        sgp[0][lev][i].print();
        }*/
}
void Query_sgt(int now,int l,int r,vector<int>& res)
{
        if (sgt[now].l==l && sgt[now].r==r)
        {
                res.push_back(now);
                return ;
        }
        if (r<=smid)
        {
                Query_sgt(lch,l,r,res);
        }else if(smid<l)
        {
                Query_sgt(rch,l,r,res);
        }else
        {
                Query_sgt(lch,l,smid,res);
                Query_sgt(rch,smid+1,r,res);
        }
}
struct Edge
{
        int np,val;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope=-1;
void addedge(int x,int y)
{
        E[++tope].np=y;
        E[tope].next=V[x];
        V[x]=&E[tope];
}
int siz[MAXN],depth[MAXN];
int pnt[MAXN];
int top[MAXN],son[MAXN];
void dfs1(int now)
{
        Edge *ne;
        int mxsiz=0;
        siz[now]=1;
        for (ne=V[now];ne;ne=ne->next)
        {
                if (ne->np==pnt[now])continue;
                pnt[ne->np]=now;
                depth[ne->np]=depth[now]+1;
                dfs1(ne->np);
                siz[now]+=siz[ne->np];
                if (siz[ne->np]>mxsiz)
                {
                        mxsiz=siz[ne->np];
                        son[now]=ne->np;
                }
        }
}
int pos[MAXN],dfstime;
void dfs2(int now)
{
        Edge *ne;
        pos[now]=++dfstime;
        dfsq[dfstime]=now;
        if (son[now])
        {
                top[son[now]]=top[now];
                dfs2(son[now]);
        }
        for (ne=V[now];ne;ne=ne->next)
        {
                if (ne->np==pnt[now] || ne->np==son[now])continue;
                top[ne->np]=ne->np;
                dfs2(ne->np);
        }
}
real search_m(int x,int y)
{
        vector<pair<real,real> > vec1,vec2;
        while (x!=y)
        {
                if (depth[x]>=depth[y])
                {
                        vec1.push_back(make_pair(v1[x],v2[x]));
                        vec2.push_back(make_pair(v3[x],v4[x]));
                        x=pnt[x];
                }else
                {
                        vec1.push_back(make_pair(v1[y],v2[y]));
                        vec2.push_back(make_pair(v3[y],v4[y]));
                        y=pnt[y];
                }
        }
        vec1.push_back(make_pair(v1[x],v2[x]));
        vec2.push_back(make_pair(v3[x],v4[x]));
        real mxv=0;
        for (int i=0;i<vec1.size();i++)
                for (int j=0;j<vec2.size();j++)
                        mxv=max(mxv,(vec1[i].first+vec2[j].first)/(vec1[i].second+vec2[j].second));
        return mxv;
}

int main()
{
        freopen("input.txt","r",stdin);
        //设原式x==p1
        //y==v1
        //p==p2
        //q==v2
        //(v1+v2)/(p1+p2)>=ans
        //(v1+v2)>=ans*p1+ans*p2
        //(v1-ans*p1) + (v2-ans*p2)>=0
        //对于二分的ans我们只需要分别处理前半部分和后半部分的最大值即可。
        //v1-ans*p1==c
        //-p1*ans+v1==c
        //原式 -x*ans+y==c
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
                scanf("%lf",v2+i);//分母1
        for (int i=1;i<=n;i++)
                scanf("%lf",v1+i);//分子1
        for (int i=1;i<=n;i++)
                scanf("%lf",v4+i);//分母2
        for (int i=1;i<=n;i++)
                scanf("%lf",v3+i);//分子2
        int x,y;
        for (int i=1;i<n;i++)
        {
                scanf("%d%d",&x,&y);
                addedge(x,y);
                addedge(y,x);
        }
        dfs1(1);
        top[1]=1;
        dfs2(1);
        Build_sgt(1,1,n,0);
        int q;
        real ans=0;
        scanf("%d",&q);
        vector<int> vec;
        for (int i=0;i<q;i++)
        {
                scanf("%d%d",&x,&y);
                vec.clear();
                while (true)
                {
                        if (top[x]==top[y])
                        {
                                if (pos[x]>pos[y])swap(x,y);
                                Query_sgt(1,pos[x],pos[y],vec);
                                break;
                        }
                        if (depth[top[x]]<depth[top[y]])swap(x,y);
                        Query_sgt(1,pos[top[x]],pos[x],vec);
                        x=pnt[top[x]];
                }
                real l=0,r=maxval;
                real mid;
                real mx1,mx2;
                while (r-l>eps)
                {
                        mid=(l+r)/2;
                        mx1=mx2=-inf;
                        pair<real,real> pr;
                        for (int j=0;j<vec.size();j++)
                        {
                                pr=sgt_getv(vec[j],mid);
                                mx1=max(mx1,pr.first);
                                mx2=max(mx2,pr.second);
                        }
                        if (mx1+mx2>=0)
                                l=mid;
                        else
                                r=mid;
                }
                printf("%.5lf\n",l+eps);
        }
}
时间: 2024-10-28 21:35:05

bzoj 2402: 陶陶的难题II 二分答案维护凸包的相关文章

BZOJ 2402 陶陶的难题II 二分答案+斜率优化+树链剖分+线段树维护凸包

题目大意:给定一棵树,每个点有两个坐标(x1,y1)和(x2,y2),多次询问某条链上选择两个点i和j(可以相同),求(y1i+y2j)/(x1i+x2j)的最大值 我竟没看出来这是01分数规划...真是老了... 二分答案ans,问题转化成验证(y1i+y2j)/(x1i+x2j)是否>=ans 将式子变形可得(y1i-ans*x1i)+(y2j-ans*x2j)>=0 加号两边独立,分别计算即可 问题转化为求链上y-ans*x最大的点 令P=y-ans*x 则y=ans*x+P 我们发现这

洛谷P1182 数列分段Section II 二分答案

洛谷P1182 数列分段Section II 二分答案 题意:将 n 个 数 分为 m段 求一种方案,使这m段中最大的和 最小 额..可能有点拗口,其实就是说每一种方案,都有对应的 每段和的最大值,要求一种方案,使最大值最小 题解 :二分答案 mid为分成的最大值, 然后O(n) 判断 答案 是否可行 贪心的做下去,如果再加上就要超过了,那就新开一段 最后判断开的段数是否小于 m 1.注意要判断 如果当前这个值大于 mid,一个值就已经大于 mid了,那就直接退出了,否则 ,这个值也只会单独算为

HDU3081Marriage Match II(二分答案+并查集+最大流SAP)经典

Marriage Match II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2507    Accepted Submission(s): 856 Problem Description Presumably, you all have known the question of stable marriage match. A

BZOJ 1196 [HNOI2006]公路修建问题(二分答案+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1196 [题目大意] 对于每条可能维修的公路可选择修一级公路或者二级公路,价值不同 要求图连通,且至少有k条一级公路时最大价值公路价值最小. [题解] 二分答案,从一级公路开始处理,利用并查集验证两个条件. [代码] #include <cstdio> #include <algorithm> using namespace std; const int N=20005;

BZOJ 4590: [Shoi2015]自动刷题机 二分答案

4590: [Shoi2015]自动刷题机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1056  Solved: 380[Submit][Status][Discuss] Description 曾经发明了信号增幅仪的发明家SHTSC又公开了他的新发明:自动刷题机--一种可以自动AC题目的神秘装置.自动 刷题机刷题的方式非常简单:首先会瞬间得出题目的正确做法,然后开始写程序,每秒,自动刷题机的代码生成模 块会有两种可能的结果: A.写了x行代

BZOJ 2440: [中山市选2011]完全平方数(二分答案 + 莫比乌斯函数 + 容斥原理)

传送门 2440: [中山市选2011]完全平方数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2693  Solved: 1307[Submit][Status][Discuss] Description 小 X 自幼就很喜欢数.但奇怪的是,他十分讨厌完全平方数.他觉得这些 数看起来很令人难受.由此,他也讨厌所有是完全平方数的正整数倍的数.然而 这丝毫不影响他对其他数的热爱. 这天是小X的生日,小 W 想送一个数给他作为生日礼物.当然他不能送

BZOJ 1863: [Zjoi2006]trouble 皇帝的烦恼( 二分答案 )

二分答案..然后从头到尾推一下, 看最后一个能不能取0个和第一个人相同的勋章 ----------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 20009; int w[maxn], Min[maxn], Ma

BZOJ 1196 HNOI 2006 公路修建问题 二分答案+并查集

题目大意:给出n个点,要求把它们连成一棵树,有一些边可供选择,每一条遍都有一级公路和二级公路,问在一级公路不少于k的情况下最高花费的最低值是多少. 思路:二分答案,然后验证的时候先将边按照一级公路的权值从大到小排序,我们每一次验证应该尽可能的选择能选的一级公路,如果一级公路不能选,看二级公路能不能选,如果.最后看一级公路选择的数量和总的公路选择的数量. CODE: #include <cstdio> #include <cstring> #include <iostream&

【模板】【P1182】数列分段II——二分答案

题意:给定一列数,分成m段,使每段和的最大值最小. 考虑二分最小段和size,答案显然满足单调性.可以在每次check中累加数列元素判断当前组的总和是否在size以内.由于序列元素均为非负整数,前缀和数组的值满足非严格单调递增,那么可以在前缀和上再套一个二分来优化暴力累加的过程. 我不知道优化以后的复杂度怎么分析,反正它跑的快多了 代码: #include <iostream> #include <cstdio> #define maxn 100010 using namespac