题目描述
一棵树, n n n 个点,每条边有权值 ( a i , b i ) (a_i,b_i) (ai,bi) ,表示在 t t t 时刻这条边的值为 a i t + b i a_it+b_i ait+bi
问对于 t ∈ [ 0 , m − 1 ] t\in[0,m-1] t∈[0,m−1],树的直径长度。
n ≤ 1 0 5 , m ≤ 1 0 6 n\le10^5,m\le10^6 n≤105,m≤106
题目分析
与最长路径有关,考虑点分治。
每个子树内的一条到点分中心的路径求和表示为一条射线 A t + B At+B At+B。
子树内的所有路径要求一个下凸形式的半平面交,但是这并不好整。
半平面交和凸包是对偶问题,可以这样转化:
d i s = a ∗ t + b dis=a*t+b dis=a∗t+b
b = − t ∗ a + d i s b=-t*a+dis b=−t∗a+dis
要 d i s dis dis 最大化,就是给定斜率 − t -t −t,最大化截距,于是可以看做对点 ( a , b ) (a,b) (a,b) 求上凸包
合并两条路径就是两个上凸包求闵可夫斯基和。
要求全局的最大凸包,两两合并的话无法承受,考虑分治合并,先分治左右两边,求出两边各自对应的最大凸包,然后将它们做闵可夫斯基和。点分治结束后,将所有的闵可夫斯基得到的凸包一起再求一次凸包,就是全局的最优凸包。
这样分治合并, T ( n ) = n log n + 2 T ( n 2 ) T(n)=n\log n+2T(\frac n2) T(n)=nlogn+2T(2n),复杂度 O ( n log 2 n ) O(n\log^2n) O(nlog2n),并且最后求凸包是如果要排序还要再加一个 log \log log,明显很卡。
如果每层只需要合并两条路径,复杂度就可以降下来,于是想到边分治,然后就很好做了,每次求出两边的凸包,然后做闵可夫斯基和,得到的所有和最后再求一次凸包。复杂度 O ( n log n + s o r t ( n log n ) ) O(n\log n+sort(n\log n)) O(nlogn+sort(nlogn))
最后求答案就按斜率递减在凸包上扫一遍即可。
边分治也不难,就是三度化,新边的 a , b = 0 a,b=0 a,b=0,然后每次找到使得两边较大子树尽可能小的边。每次至少减少 1 3 \frac 13 31。新加的点不是叶子,并且此题 a , b ≥ 0 a,b\ge 0 a,b≥0,所以终点为新点的路径可以不用加进凸包中。
Code:
#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
using namespace std;
int n,m,N,nn,siz[maxn],An,Bn,Pn;
bool vis[maxn<<1];
struct Point{
LL x,y; Point(LL x=0,LL y=0):x(x),y(y){}
Point operator + (const Point &p){return Point(x+p.x,y+p.y);}
Point operator - (const Point &p){return Point(x-p.x,y-p.y);}
LL operator * (const Point &p){return x*p.y-y*p.x;}
bool operator < (const Point &p)const{return x==p.x?y<p.y:x<p.x;}
}w[maxn<<1],A[maxn],B[maxn],P[maxn*15];
void Convex(Point *a,int &n){
static Point q[maxn*15]; int tp=0;
sort(a+1,a+1+n);
for(int i=1;i<=n;q[++tp]=a[i],i++) while(tp>1&&(q[tp]-q[tp-1])*(a[i]-q[tp-1])>=0) tp--;
memcpy(a,q,((n=tp)+1)*sizeof q[0]);
}
void Merge(){
if(!An||!Bn) return;
int i=1,j=1; P[++Pn]=A[i]+B[j];
while(i<An||j<Bn) j==Bn||(i<An&&(A[i+1]-A[i])*(B[j+1]-B[j])<=0)?i++:j++, P[++Pn]=A[i]+B[j];
}
vector<pair<int,Point> >G[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot=1;
void Line(int x,int y,Point z){
nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;
nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,w[tot]=z;
}
void rebuild(int u,int ff){
int v,p=0; Point w;
for(auto i:G[u]) if((v=i.first)!=ff){
if(p) Line(p,++N,0),Line(p=N,v,i.second);
else Line(u,v,i.second),p=u;
rebuild(v,u);
}
}
void getrt(int u,int ff,int tsz,int &mn,int &e){
siz[u]=1;
for(int i=fir[u],v,t;i;i=nxt[i]) if(!vis[i]&&(v=to[i])!=ff){
getrt(v,u,tsz,mn,e),siz[u]+=siz[v];
if((t=max(siz[v],tsz-siz[v]))<mn) mn=t,e=i;
}
}
int getrt(int u,int tsz){int mn=1e9; return getrt(u,0,tsz,mn,u),u;}
void dfs(int u,int ff,Point *a,int &cnt,Point s){
if(u<=nn) a[++cnt]=s;
for(int i=fir[u],v;i;i=nxt[i]) if(!vis[i]&&(v=to[i])!=ff) dfs(v,u,a,cnt,s+w[i]);
}
void TDC(int e,int tsz){
int u=to[e],v=to[e^1];
vis[e]=vis[e^1]=1;
dfs(u,0,A,An=0,w[e]),dfs(v,0,B,Bn=0,0);
Convex(A,An),Convex(B,Bn),Merge();
int L=siz[u],R=tsz-siz[u];
if(L>1) TDC(getrt(u,L),L);
if(R>1) TDC(getrt(v,R),R);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,x,y,a,b;i<n;i++)
scanf("%d%d%d%d",&x,&y,&a,&b),
G[x].push_back(make_pair(y,Point(a,b))),G[y].push_back(make_pair(x,Point(a,b)));
N=nn=n,rebuild(1,0),n=N;
TDC(getrt(1,n),n);
Convex(P,Pn);
for(int t=0,i=1;t<m;t++){
Point k=Point(1,-t);
while(i<Pn&&(P[i+1]-P[i])*k<=0) i++;
printf("%I64d%c",P[i].x*t+P[i].y,t==m-1?10:32);
}
}