[二进制分组 dsu on tree 二次函数] Codechef KILLER Painting Tree

来源:互联网 发布:泛微java怎么样 编辑:程序博客网 时间:2024/05/29 18:55

首先我们可以列一个dp 按深度 fu表示链剖分中选择了从u到当前深度的祖先这条链所能得到的最小答案
我们发现这条链的贡献是一个关于祖先深度二次函数 具体形式我忘了 而其他贡献则是一路上来兄弟子树中的最大值累加
这样就很明确了 是要求对每一个点维护一个二次函数 还要资瓷子树加 查询子树中x=k最小值

首先询问一坨二次函数最大值 有一个经典做法来自 BZOJ2646
我们可以维护n条函数的下轮廓 这个轮廓应该是O(n)段的
注意这个轮廓是资瓷合并的 O(n)段和O(n)段可以在O(n)时间内合并 具体就是从左往右求交点比较下谁小咯 实现起来有点细节

但是我们发现这个轮廓是不资瓷插入一条二次函数的 这样的复杂度还是O(n) 但是我们可以二进制分组 这样就能在均摊O(logn)完成插入
资瓷插入有什么好呢 我们使用dsu on tree 的技巧 就能够总复杂度O(nlog2n)

这样子树加就能够很轻易的实现了

常数感人

#include<cstdio>#include<cstdlib>#include<cmath>#include<vector>#include<algorithm>#include<cstring>#include<iostream>#include<iomanip>#define PB push_back#define cl(x) memset(x,0,sizeof(x))using namespace std;typedef long long ll;typedef long double ld;inline char nc(){  static char buf[100000],*p1=buf,*p2=buf;  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline void read(int &x){  char c=nc(),b=1;  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;}inline void read(ld &x){  char c=nc(),b=1;  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;}const int N=100005;struct edge{  int u,v,next;}G[N<<1];int head[N],inum;inline void add(int u,int v,int p){  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;}#define V G[p].vint n;ld H[N],C[N],d[N],h;int size[N],son[N];inline void dfs(int u,int fa){  h=max(h,d[u]); size[u]=1;  for (int p=head[u];p;p=G[p].next)    if (V!=fa)      d[V]=d[u]+1,dfs(V,u),size[u]+=size[V];  int maxv=0; son[u]=0;  for (int p=head[u];p;p=G[p].next)    if (V!=fa && size[V]>maxv)      maxv=size[son[u]=V];}const ld eps=1e-11;const ld oo=1e6;struct Func{  ld a,b,c;  Func() { }  Func(ld a,ld b,ld c):a(a),b(b),c(c) { }  ld f(ld x){    return a*x*x+b*x+c;  }}F[N];struct P{  ld l,r; int p;  P(){ }  P(ld l,ld r,int p):l(l),r(r),p(p) { }};inline ld pos(const Func &a,const Func &b,ld l,ld r){  ld A=a.a-b.a,B=a.b-b.b,C=a.c-b.c;  if (fabs(A)<eps){    if (fabs(B)<eps) return r;    ld x=-C/B;    if (x>l+eps&&x<r) return x;    return r;  }  ld d=B*B-4*A*C;  if (d<-eps) return r;  d=sqrt(d);  ld x=(-B-d)/(2*A),y=(-B+d)/(2*A),ret=r;  if (x>l+eps&&x<r) ret=x;  if (y>l+eps&&y<r) ret=min(ret,y);  return ret;}struct Bin{  vector<Func> e; int tot;  vector<ld> q; vector<P> c;  void merge(vector<P>&f,const vector<P>&a,const vector<P>&b){    int na=a.size(),nb=b.size(),pa=0,pb=0;    c.clear(); q.clear(); q.PB(-oo);    while (pa<na && pb<nb)      if (a[pa].r<b[pb].r || pb==nb){    if (a[pa].r>q.back()+eps) q.PB(a[pa].r); pa++;      }else{    if (b[pb].r>q.back()+eps) q.PB(b[pb].r); pb++;      }    while (pa<na){      if (a[pa].r>q.back()+eps) q.PB(a[pa].r); pa++;    }    while (pb<nb){      if (b[pb].r>q.back()+eps) q.PB(b[pb].r); pb++;    }    for (int i=0,j=0,k=0;k<q.size()-1;k++){      ld l=q[k],r=q[k+1];      while (i<na && a[i].r+eps<r) i++;      while (j<nb && b[j].r+eps<r) j++;      if (i==na || a[i].l>l+eps){ c.PB(P(l,r,b[j].p)); continue; }      if (j==nb || b[j].l>l+eps){ c.PB(P(l,r,a[i].p)); continue; }      while (l+eps<r){    ld m=pos(e[a[i].p],e[b[j].p],l,r),mid=(l+m)/2;    if (e[a[i].p].f(mid)<e[b[j].p].f(mid))      c.PB(P(l,m,a[i].p));    else      c.PB(P(l,m,b[j].p));    l=m;      }    }    for (int i=0,j;i<c.size();i=j){      j=i; while (j<c.size() && c[i].p==c[j].p) j++;      f.PB(P(c[i].l,c[j-1].r,c[i].p));    }  }  ld query(const vector<P>&v,ld o){    int l=0,mid,r=v.size()-1;    while (l<=r){      mid=(l+r)>>1;      if (v[mid].r+eps<o)    l=mid+1;      else if (v[mid].l-eps>o)    r=mid-1;      else    return e[v[mid].p].f(o);    }  }  ld tag;  vector<P> v[20];  int cnt,size[20];  Bin(){    e.PB(Func()); tot=0; tag=0; cnt=0;  }  void clear(){    for (int i=1;i<=cnt;i++) v[i].clear();     cnt=0; tot=0; tag=0; e.clear(); e.PB(Func());  }  vector<P> tmp;  void Add(Func a){    a.c-=tag;    e.PB(a); ++tot;    ++cnt; size[cnt]=1; v[cnt].PB(P(-oo,oo,tot));    while (cnt>1 && size[cnt]==size[cnt-1]){      tmp.clear(); merge(tmp,v[cnt],v[cnt-1]);      v[cnt-1]=tmp; v[cnt].clear(); cnt--; size[cnt]<<=1;    }  }  ld Query(ld x){    ld ans=1e30;    for (int i=1;i<=cnt;i++)      ans=min(ans,query(v[i],x));    return ans+tag;  }}B[N];int bi[N];int bcnt;ld tag[N];ld minv[N];inline void Add(int u,int fa,ld T,Bin &B){  T+=tag[u];  F[u].c+=T;  B.Add(F[u]);  F[u].c-=T;  for (int p=head[u];p;p=G[p].next)    if (V!=fa)      Add(V,u,T,B);}inline void dp(int u,int fa){  for (int p=head[u];p;p=G[p].next)    if (V!=fa)      dp(V,u);  ld sum=0;  for (int p=head[u];p;p=G[p].next)    if (V!=fa){      minv[V]=B[bi[V]].Query(d[V]);      sum+=minv[V];    }  if (son[u]){    bi[u]=bi[son[u]];    B[bi[u]].tag+=sum-minv[son[u]];    tag[son[u]]+=sum-minv[son[u]];    for (int p=head[u];p;p=G[p].next)      if (V!=fa && V!=son[u]){    tag[V]+=sum-minv[V];    Add(V,u,0,B[bi[u]]);      }  }else{    bi[u]=++bcnt;  }  F[u].a=C[u]/2.0;  F[u].b=-C[u]*C[u]-C[u]*h-C[u]/2.0;  F[u].c=-C[u]*d[u]*(d[u]+1)/2.0-H[u]+sum+(d[u]+1)*(C[u]*C[u]+C[u]*h);  B[bi[u]].Add(F[u]);}int main(){  freopen("t.in","r",stdin);  freopen("t.out","w",stdout);  int T,x,y;  read(T);  while (T--){    read(n);    for (int i=1;i<=n;i++) read(H[i]),read(C[i]);    for (int i=1;i<n;i++) read(x),read(y),add(x,y,++inum),add(y,x,++inum);    h=0; dfs(1,0);    dp(1,0);    ld ans=B[bi[1]].Query(0);    printf("%.0lf\n",(double)ans);    //cout<<fixed<<setprecision(0)<<ans<<endl;    cl(head); inum=0; cl(tag); cl(bi);    for (int i=1;i<=bcnt;i++) B[i].clear(); bcnt=0;  }  return 0;}
原创粉丝点击