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
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
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[][][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[][][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[][sgt[now].lev][sgt[now].l+],&sgp[][sgt[now].lev][sgt[now].l+sgt[now].tot0],point(ps,-inf),cmp_point_x)
- -sgp[][sgt[now].lev]-;
- res.first=sgc[][sgt[now].lev][x].get_h(ps);
- x=lower_bound(&sgp[][sgt[now].lev][sgt[now].l+],&sgp[][sgt[now].lev][sgt[now].l+sgt[now].tot1],point(ps,-inf),cmp_point_x)
- -sgp[][sgt[now].lev]-;
- res.second=sgc[][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=;
- 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<=)
- {
- res[totr]=lnow;
- if (totr)
- pres[totr]=crossover(res[totr-],res[totr]);
- totr++;
- }else
- {
- while (totr> && crossover(res[totr-],lnow).y>=pres[totr-].y)totr--;
- res[totr]=lnow;
- pres[totr]=crossover(res[totr-],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[][lev][l]=line(-v2[dfsq[l]],v1[dfsq[l]]);
- sgc[][lev][l]=line(-v4[dfsq[l]],v3[dfsq[l]]);
- sgt[now].tot0=sgt[now].tot1=;
- // 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+);
- Build_sgt(rch,smid+,r,lev+);
- Combine(&sgc[][lev][l],&sgp[][lev][l],sgt[now].tot0,&sgc[][lev+][l],sgt[lch].tot0,&sgc[][lev+][smid+],sgt[rch].tot0);
- Combine(&sgc[][lev][l],&sgp[][lev][l],sgt[now].tot1,&sgc[][lev+][l],sgt[lch].tot1,&sgc[][lev+][smid+],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+,r,res);
- }
- }
- struct Edge
- {
- int np,val;
- Edge *next;
- }E[MAXE],*V[MAXV];
- int tope=-;
- 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=;
- siz[now]=;
- for (ne=V[now];ne;ne=ne->next)
- {
- if (ne->np==pnt[now])continue;
- pnt[ne->np]=now;
- depth[ne->np]=depth[now]+;
- 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=;
- for (int i=;i<vec1.size();i++)
- for (int j=;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=;i<=n;i++)
- scanf("%lf",v2+i);//分母1
- for (int i=;i<=n;i++)
- scanf("%lf",v1+i);//分子1
- for (int i=;i<=n;i++)
- scanf("%lf",v4+i);//分母2
- for (int i=;i<=n;i++)
- scanf("%lf",v3+i);//分子2
- int x,y;
- for (int i=;i<n;i++)
- {
- scanf("%d%d",&x,&y);
- addedge(x,y);
- addedge(y,x);
- }
- dfs1();
- top[]=;
- dfs2();
- Build_sgt(,,n,);
- int q;
- real ans=;
- scanf("%d",&q);
- vector<int> vec;
- for (int i=;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(,pos[x],pos[y],vec);
- break;
- }
- if (depth[top[x]]<depth[top[y]])swap(x,y);
- Query_sgt(,pos[top[x]],pos[x],vec);
- x=pnt[top[x]];
- }
- real l=,r=maxval;
- real mid;
- real mx1,mx2;
- while (r-l>eps)
- {
- mid=(l+r)/;
- mx1=mx2=-inf;
- pair<real,real> pr;
- for (int j=;j<vec.size();j++)
- {
- pr=sgt_getv(vec[j],mid);
- mx1=max(mx1,pr.first);
- mx2=max(mx2,pr.second);
- }
- if (mx1+mx2>=)
- l=mid;
- else
- r=mid;
- }
- printf("%.5lf\n",l+eps);
- }
- }
bzoj 2402: 陶陶的难题II 二分答案维护凸包的更多相关文章
- [BZOJ 2653] middle(可持久化线段树+二分答案)
[BZOJ 2653] middle(可持久化线段树+二分答案) 题面 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整. 给你一个长度为n的序 ...
- BZOJ 1570: [JSOI2008]Blue Mary的旅行( 二分答案 + 最大流 )
二分答案, 然后对于答案m, 把地点分成m层, 对于边(u, v), 第x层的u -> 第x+1层的v 连边. 然后第x层的u -> 第x+1层的u连边(+oo), S->第一层的1 ...
- BZOJ 1196 [HNOI2006]公路修建问题(二分答案+并查集)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1196 [题目大意] 对于每条可能维修的公路可选择修一级公路或者二级公路,价值不同 要求 ...
- BZOJ 1863: [Zjoi2006]trouble 皇帝的烦恼( 二分答案 )
二分答案..然后从头到尾推一下, 看最后一个能不能取0个和第一个人相同的勋章 ------------------------------------------------------------- ...
- BZOJ:1816 [Cqoi2010]扑克牌 (贪心或二分答案)
题面 \(solution:\) 这道题难就难在你能否读懂题目的意思,我们将它翻译一下: 现在我有n根竹子(每根竹子有\(c_i\)节,每节竹子高度为1),我可以通过消耗一点法力值使某一根竹子的某两节 ...
- BZOJ 4590 [Shoi2015]自动刷题机 ——二分答案
二分答案水题. #include <cstdio> #include <cstring> #include <iostream> #include <algo ...
- BZOJ 4552 [Tjoi2016&Heoi2016]排序 ——线段树 二分答案
听说是BC原题. 好题,二分答案变成01序列,就可以方便的用线段树维护了. 然后就是区间查询和覆盖了. #include <map> #include <cmath> #inc ...
- Bzoj 1926: [Sdoi2010]粟粟的书架(二分答案+乱搞+主席树)
1926: [Sdoi2010]粟粟的书架 Time Limit: 30 Sec Memory Limit: 552 MB Description 幸福幼儿园 B29 班的粟粟是一个聪明机灵.乖巧可爱 ...
- poj2018 Best Cow Fences[二分答案or凸包优化]
题目. 首先暴力很好搞,但是优化的话就不会了.放弃QWQ. 做法1:二分答案 然后发现平均值是$ave=\frac{sum}{len}$,这种形式似乎可以二分答案?把$len$移到左边. 于是二分$a ...
随机推荐
- Amazon EC2上搭建VPN服务器
Amazon EC2 提供了一年免费试用,Micro Instance,配置是 1G 内存,共享 CPU,和每月 15G 的流量.搭一个 VPN 服务器绰绰有余了.操作系统我选的是 Amazon Li ...
- 鸭子类型duck typing(动态)
在程序设计中,鸭子类型(duck typing)是动态类型的一种风格.在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定.这个概念的名字来源于由Ja ...
- C# select的联动效果
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html><head&g ...
- 20160410javaweb之JDBC---DBUtils框架
DBUtils 1.DbUtils 工具类 2.QueryRunner -- 两行代码搞定增删改查 (1)QueryRunner() --需要控制事务时,使用这组方法 int update(Conne ...
- jdbc-connect-oracle12c-pdb/cdb(jdbc连接oracle12c的pdb和cdb)
1 本文简介: 通过特意引发问题,聚焦问题,解决问题,并循序渐进 最后总结jdbc连接oracle12c中cdb和pdb的条件. 软件环境:Redhat7.1+orcacle12c 2 ...
- switch case实现两个数的算术运算
方法一: package com.liaojianya.chapter1; import java.util.Scanner; public class SwitchDemo1 { public st ...
- Codevs 1066 引水入城 2010年NOIP全国联赛提高组
1066 引水入城 2010年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 传送门 题目描述 Description 在一个遥远的国度 ...
- QListWidget 删除选中项目
void MainWindow::on_action_Del_triggered() { QList<QListWidgetItem*> selectedItems = ui->li ...
- 计数排序之python 实现源码
old = [2, 5, 3, 0, 2, 3, 0, 3] new = [0, 0, 0, 0, 0, 0] for i in range(len(old)): new[old[i]] = new[ ...
- 数据结构学习——shell排序的C语言实现
shell排序: 这个排序的命名是来自发明者的名字,和排序的方法没有字面上的联系.所以不要因为名字而感觉很难.在K&R的C程序设计语言中书中只用了几行代码很简洁的实现了这个排序算法.那就来看看 ...