【树链剖分+维护凸线】Saber

来源:互联网 发布:诚实国度的爱丽丝 知乎 编辑:程序博客网 时间:2024/05/17 07:49

秋哥是saber控,每次saber都很难,但是还有两种颜色的saber没出。

有一棵 n 个点的树,每个点有四个权值 x,y,p,q,给出 m 个询问(a,b),假设 i,j 为 a 到
b 的路径上的可以重合的两个点,求(yi+qj)/(xi+pj)的最大值。

二分答案,就化出了(x,y)与(p,q)分开的式子,分别对此两式求最大值判断,用斜率的经典分析来看(x,y)

若有一k比i优,则

yk-ans*xk>=yi-ans*xi

yk-yi>=ans*(xk-xi)

(yk-yi)/(xk-xi)>=ans

因此维护一个斜率单减的队列,每次二分<ans的位置即可找到当前区间最大值,可是这是一颗树,很自然想到树链剖分,但是整个区间二分肯定包含非此路径节点,于是用线段树维护恰好包含的区间,每个节点还得额外储存队列,这里面有多少log我也不想搞清了,反正最外面那层二分不改成迭代对于秋哥的数据会卡着时间超3个点。

#include <cstdio>#include <cstring>#include <cstdlib>const int oo=1073741819,maxn=100000;const double eps=1e-3;int t[2],rt[maxn],f[maxn],d[maxn],next[maxn],sora[maxn],tail[maxn],o[maxn],s1,ss,n,m,p[maxn],up[maxn],m1;double sum[2],ans[2];struct lisan{int o,d,i;}b[65536];struct room{double x,y;}u[2][300000];struct inf{double x,y,p,q;}a[maxn];inline double max(double x,double y) {return (x>y) ? x : y;}struct seg{  int h,r,k;  double check(room a,room b) {return (b.y-a.y)/(b.x-a.x);}  inline void ori(int l1,int r1,int l2,int r2)  {    room ne;    h=r=++t[k];    for (r--;(((l1<=r1)&&(r1))||((l2<=r2)&&(r2)));) {      if (((u[k][l1].x<u[k][l2].x)&&(l1<=r1))||(l2>r2)) ne=u[k][l1],l1++;else ne=u[k][l2],l2++;      for (;(h<r)&&(check(u[k][r],ne)>check(u[k][r-1],u[k][r]));r--) ;      u[k][++r]=ne;    }    if (h>r) r=0,h=1;else t[k]=r;  }  inline double search(double x)  {    int ll,rr,mid;    for (ll=h+1,rr=r;ll<=rr;) {      mid=(ll+rr)>>1;      if (check(u[k][mid-1],u[k][mid])>x) ll=mid+1;else rr=mid-1;    }    if (ll<=r) return max(u[k][ll-1].y-x*u[k][ll-1].x,u[k][ll].y-x*u[k][ll].x);    else return u[k][ll-1].y-x*u[k][ll-1].x;  }}c[2][65536];inline void dfs(int x,int y,int dep){  int i,ne;  rt[x]=y,f[x]=1,d[x]=dep;  int max=-oo,maxi=0;  for (i=x;next[i]!=0;) {    i=next[i],ne=sora[i];    if (ne!=y) {      dfs(ne,x,dep+1),f[x]+=f[ne];      if (f[ne]>max) max=f[ne],maxi=ne;    }  }  if (maxi!=0) o[x]=o[maxi];else o[x]=++s1;}inline int cmp(const void *i,const void *j){  lisan p=*(lisan *)i,q=*(lisan *)j;  if (p.o!=q.o) return p.o-q.o;  return p.d-q.d;}void ori(){  int i;  for (i=1;i<=n;i++) b[i].i=i,b[i].o=o[i],b[i].d=d[i];  qsort(b+1,n,sizeof(b[1]),cmp);  for (i=1;i<=n;i++) {    p[b[i].i]=i;    if (b[i].o!=b[i-1].o) up[b[i].o]=b[i].i;  }  for (i=1;i<=n;i++) {    c[0][i+m1].h=c[0][i+m1].r=++t[0],c[1][i+m1].h=c[1][i+m1].r=++t[1];    c[0][i+m1].k=0,c[1][i+m1].k=1;    u[0][t[0]].x=a[b[i].i].x,u[0][t[0]].y=a[b[i].i].y,    u[1][t[1]].x=a[b[i].i].p,u[1][t[1]].y=a[b[i].i].q;  }  for (i=m1-1;i>=1;i--) {    c[0][i].k=0,c[1][i].k=1;    c[0][i].ori(c[0][i<<1].h,c[0][i<<1].r,c[0][(i<<1)+1].h,c[0][(i<<1)+1].r);    c[1][i].ori(c[1][i<<1].h,c[1][i<<1].r,c[1][(i<<1)+1].h,c[1][(i<<1)+1].r);  }}void origin() {  int i;  for (m1=1;m1<=n+2;m1<<=1) ;t[0]=t[1]=m1+m1;  for (i=1;i<=n;i++) tail[i]=i;ss=n;}inline double ask(int k,int l,int r,double x){  double ans;  l+=m1-1,r+=m1+1,ans=-oo;  for (;!(1==(l^r));l>>=1,r>>=1) {    if (0==(l&1)) ans=max(ans,c[k][l+1].search(x));    if (1==(r&1)) ans=max(ans,c[k][r-1].search(x));  }  return ans;}inline void updata(int &l,double x){    sum[0]=ask(0,p[up[o[l]]],p[l],x);sum[1]=ask(1,p[up[o[l]]],p[l],x);    ans[0]=max(ans[0],sum[0]);ans[1]=max(ans[1],sum[1]);    l=rt[up[o[l]]];}inline int find(double x,int l,int r){  int e;  ans[0]=ans[1]=-oo;  for (;;) {    if (o[l]==o[r]) {      if (d[l]<d[r]) e=l,l=r,r=e;      sum[0]=ask(0,p[r],p[l],x);sum[1]=ask(1,p[r],p[l],x);      ans[0]=max(ans[0],sum[0]);ans[1]=max(ans[1],sum[1]);      break;    }    else       if (d[up[o[l]]]>=d[up[o[r]]]) updata(l,x);else updata(r,x);  }  if (ans[0]+ans[1]>0) return 1;else return 0;}inline void link(int x,int y){  ss++,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y;  ss++,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x;}inline int equ(double x,double y) {return ((y-x)>-eps)&&((y-x)<eps);}void init(){  int i,x,y;  double l,r,mid;  scanf("%d\n",&n);  origin();  for (i=1;i<=n;i++) scanf("%llf",&a[i].x);  for (i=1;i<=n;i++) scanf("%llf",&a[i].y);    for (i=1;i<=n;i++) scanf("%llf",&a[i].p);    for (i=1;i<=n;i++) scanf("%llf",&a[i].q);    for (i=1;i<=n-1;i++) scanf("%d%d",&x,&y),link(x,y);  dfs(1,0,0);  ori();  scanf("%d\n",&m);  for (i=1;i<=m;i++) {    scanf("%d%d\n",&x,&y);    for (l=0,r=100000;!equ(l,r);) {      mid=(l+r)/2;      if (find(mid,x,y)) l=mid;else r=mid;    }    printf("%.4llf\n",l);  }}int main(){  freopen("saber.in","r",stdin);  freopen("saber.out","w",stdout);    init();  return 0;}


原创粉丝点击