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
题解:想到了分数规划、凸包,没想到线段树,md这题没有修改操作啊!
先二分答案mid,那么我们想要找到路径上是否有两个点的$yi+qj-mid*(xi+pj)>0$,那么显然我们要找的就是$yi-mid*xi$和$qj-mid*pj$的最大值,可以分开处理。
发现yi-mid*xi的最大值可以用凸包来搞。我们用树剖+线段树,线段树的每个节点处都维护了它的区间中所有点形成的上凸包。我们在查询时先找到线段树上的对应区间,然后在凸包上二分即可。
总复杂度:二分、树剖、线段树、树上二分,各一个log,所以是$O(nlog_n^4)$的,居然能过(其中的有些log常数比较小)。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define lson x<<1 #define rson x<<1|1 using namespace std; typedef long double ld; const int maxn=30010; const ld eps=1e-10; int n,m,tot,cnt; int head[maxn],to[maxn<<1],next[maxn<<1],dep[maxn],siz[maxn],fa[maxn],top[maxn],p[maxn],q[maxn],rt[maxn],son[maxn]; struct node { double x,y; node() {} node(double a,double b) {x=a,y=b;} node operator - (const node &a) const {return node(x-a.x,y-a.y);} ld operator * (const node &a) const {return (ld)x*a.y-(ld)y*a.x;} }; bool cmpx(const node &a,const node &b) { return a.x<b.x; } struct sag { node v[maxn]; vector<node> s[maxn<<2]; int top[maxn<<2]; void build(int l,int r,int x) { int i; for(i=l;i<=r;i++) s[x].push_back(v[q[i]]); sort(s[x].begin(),s[x].end(),cmpx); for(top[x]=0,i=0;i<(int)s[x].size();i++) { while(top[x]>1&&(s[x][top[x]-1]-s[x][top[x]-2])*(s[x][i]-s[x][top[x]-1])>-eps) top[x]--; s[x][top[x]++]=s[x][i]; } if(l==r) return ; int mid=(l+r)>>1; build(l,mid,lson),build(mid+1,r,rson); } double query(int l,int r,int x,int a,int b,double k) { int mid; if(a<=l&&r<=b) { l=0,r=top[x]-1; while(l<r) { mid=(l+r)>>1; if(ld(s[x][mid+1].y-s[x][mid].y)<ld(s[x][mid+1].x-s[x][mid].x)*k+eps) r=mid; else l=mid+1; } return s[x][r].y-s[x][r].x*k; } mid=(l+r)>>1; if(b<=mid) return query(l,mid,lson,a,b,k); if(a>mid) return query(mid+1,r,rson,a,b,k); return max(query(l,mid,lson,a,b,k),query(mid+1,r,rson,a,b,k)); } }s1,s2; void dfs1(int x) { siz[x]=1; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]) { fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs1(to[i]),siz[x]+=siz[to[i]]; if(siz[to[i]]>siz[son[x]]) son[x]=to[i]; } } void dfs2(int x,int tp) { top[x]=tp,p[x]=++p[0],q[p[0]]=x; if(son[x]) dfs2(son[x],tp); for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]); } inline double ask(int x,int y,double k) { double a1=-1e16,a2=-1e16; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); a1=max(a1,s1.query(1,n,1,p[top[x]],p[x],k)); a2=max(a2,s2.query(1,n,1,p[top[x]],p[x],k)); x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); a1=max(a1,s1.query(1,n,1,p[x],p[y],k)); a2=max(a2,s2.query(1,n,1,p[x],p[y],k)); return a1+a2; } inline void add(int a,int b) { to[cnt]=b,next[cnt]=head[a],head[a]=cnt++; } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar(); return ret*f; } int main() { //freopen("bz2402.in","r",stdin); n=rd(); int i,a,b; for(i=1;i<=n;i++) scanf("%lf",&s1.v[i].x); for(i=1;i<=n;i++) scanf("%lf",&s1.v[i].y); for(i=1;i<=n;i++) scanf("%lf",&s2.v[i].x); for(i=1;i<=n;i++) scanf("%lf",&s2.v[i].y); memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a); dep[1]=1,dfs1(1),dfs2(1,1); s1.build(1,n,1),s2.build(1,n,1); m=rd(); for(i=1;i<=m;i++) { a=rd(),b=rd(); double l=0,r=1e8,mid; while(r-l>1e-5) { mid=(l+r)/2; if(ask(a,b,mid)>=0) l=mid; else r=mid; } printf("%.5lf\n",l); } return 0; }//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
| 欢迎来原网站坐坐! >原文链接<