首先我们可以列一个dp 按深度
fu
表示链剖分中选择了从
u
到当前深度的祖先这条链所能得到的最小答案
我们发现这条链的贡献是一个关于祖先深度二次函数 具体形式我忘了 而其他贡献则是一路上来兄弟子树中的最大值累加
这样就很明确了 是要求对每一个点维护一个二次函数 还要资瓷子树加 查询子树中
首先询问一坨二次函数最大值 有一个经典做法来自 BZOJ2646
我们可以维护
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].v
int 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;
}