边分治学习笔记

边分治学习笔记

用途

边分治和点分治类似,每次选取一条边,将树尽可能均匀地分成两部分,然后再去递归左右两个子树统计信息。

如果直接在原树上进行边分治,遇到菊花图的时候复杂度就假了。

所以要把原树转成一棵二叉树再进行分治。

具体的做法是:如果一个点的儿子个数小于等于两个就直接建树,否则新建两个节点,把原树中的儿子按照奇偶性分给两个儿子。

新建出来的点根据题目要求给予恰当的信息即可。

边分治的优点在于每次只需要处理两个子树合并后的信息,更好维护一些。

例题:【BZOJ2870】最长道路tree

题目传送门

分析

路径统计的问题不难想到用分治去解决。

这题用边分治可能更好写一些,如果用点分治的话还要套数据结构。

考虑把经过某条边的路径合并。

我们把两边子树的从根出发的路径都提出来,这样的话问题转化成:

每条链有长度和权值两个属性,把两个链合并得到的是权值的最小值乘上长度和。

对两个子树内的路径按照权值排序后分别用双指针扫一遍即可。

代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=2e5+5;
int h[maxn],tot=2,n,cnt,a[maxn];
struct asd{
	int to,nxt,val;
}b[maxn];
void ad(rg int aa,rg int bb,rg int cc){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	b[tot].val=cc;
	h[aa]=tot++;
}
std::vector<int> son[maxn];
void dfs(rg int now,rg int lat){
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		son[now].push_back(u);
		dfs(u,now);
	}
}
void rebuild(){
	tot=2;
	memset(h,-1,sizeof(h));
	for(rg int now=1;now<=n;now++){
		if(son[now].size()<=2){
			for(rg int i=0,len=son[now].size();i<len;i++){
				rg int u=son[now][i];
				ad(now,u,1),ad(u,now,1);
			}
		} else {
			rg int ac1=++n,ac2=++n;
			a[ac1]=a[ac2]=a[now];
			ad(now,ac1,0),ad(ac1,now,0);
			ad(now,ac2,0),ad(ac2,now,0);
			for(rg int i=0,len=son[now].size();i<len;i++){
				if(i&1) son[ac2].push_back(son[now][i]);
				else son[ac1].push_back(son[now][i]);
			}
		}
	}
}
int maxsiz,rt,totsiz,siz[maxn];
bool vis[maxn];
void getroot(rg int now,rg int lat){
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(vis[i>>1] || u==lat) continue;
		getroot(u,now);
		siz[now]+=siz[u];
		rg int cs=std::max(siz[u],totsiz-siz[u]);
		if(cs<maxsiz){
			maxsiz=cs,rt=i;
		}
	}
}
struct jie{
	int val,dep;
	jie(){}
	jie(rg int aa,rg int bb){
		val=aa,dep=bb;
	}
}sta[2][maxn];
int tp[2];
void getdis(rg int op,rg int now,rg int lat,rg int dep,rg int val){
	val=std::min(val,a[now]);
	sta[op][++tp[op]]=jie(val,dep);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(vis[i>>1] || u==lat) continue;
		getdis(op,u,now,dep+b[i].val,val);
	}
}
bool cmp(jie aa,jie bb){
	return aa.val>bb.val;
}
long long ans=0;
void solve(rg int now,rg int sz){
	totsiz=sz,maxsiz=0x3f3f3f3f;
	getroot(now,0);
	if(maxsiz==0x3f3f3f3f) return;
	vis[rt>>1]=1;
	tp[0]=tp[1]=0;
	getdis(0,b[rt].to,0,0,0x3f3f3f3f);
	getdis(1,b[rt^1].to,0,0,0x3f3f3f3f);
	std::sort(sta[0]+1,sta[0]+tp[0]+1,cmp);
	std::sort(sta[1]+1,sta[1]+tp[1]+1,cmp);
	for(rg int i=1,j=1,maxdep=0;i<=tp[0];i++){
		while(j<=tp[1] && sta[1][j].val>=sta[0][i].val){
			maxdep=std::max(maxdep,sta[1][j].dep);
			j++;
		}
		if(j!=1){
			ans=std::max(ans,1LL*sta[0][i].val*(maxdep+sta[0][i].dep+1+b[rt].val));
		}
	}
	for(rg int i=1,j=1,maxdep=0;i<=tp[1];i++){
		while(j<=tp[0] && sta[0][j].val>=sta[1][i].val){
			maxdep=std::max(maxdep,sta[0][j].dep);
			j++;
		}
		if(j!=1){
			ans=std::max(ans,1LL*sta[1][i].val*(maxdep+sta[1][i].dep+1+b[rt].val));
		}
	}
	rg int nsz=siz[b[rt].to],nrt=rt;
	solve(b[nrt].to,nsz);
	solve(b[nrt^1].to,sz-nsz);
}
int main(){
	memset(h,-1,sizeof(h));
	n=cnt=read();
	for(rg int i=1;i<=n;i++) a[i]=read();
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read();
		ad(aa,bb,1);
		ad(bb,aa,1);
	}
	dfs(1,0);
	rebuild();
	solve(1,n);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-03-31 18:08  liuchanglc  阅读(105)  评论(0编辑  收藏  举报