边分治学习笔记
边分治学习笔记
用途
边分治和点分治类似,每次选取一条边,将树尽可能均匀地分成两部分,然后再去递归左右两个子树统计信息。
如果直接在原树上进行边分治,遇到菊花图的时候复杂度就假了。
所以要把原树转成一棵二叉树再进行分治。
具体的做法是:如果一个点的儿子个数小于等于两个就直接建树,否则新建两个节点,把原树中的儿子按照奇偶性分给两个儿子。
新建出来的点根据题目要求给予恰当的信息即可。
边分治的优点在于每次只需要处理两个子树合并后的信息,更好维护一些。
例题:【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;
}