CF1019E Raining season【半平面交转凸包,边分治】

题目描述

一棵树, 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,m1],树的直径长度。

n ≤ 1 0 5 , m ≤ 1 0 6 n\le10^5,m\le10^6 n105,m106

题目分析

与最长路径有关,考虑点分治。

每个子树内的一条到点分中心的路径求和表示为一条射线 A t + B At+B At+B

子树内的所有路径要求一个下凸形式的半平面交,但是这并不好整。

半平面交和凸包是对偶问题,可以这样转化:

d i s = a ∗ t + b dis=a*t+b dis=at+b

b = − t ∗ a + d i s b=-t*a+dis b=ta+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,b0,所以终点为新点的路径可以不用加进凸包中。

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);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值