边分治学习笔记
对于某些点分治不太好合并两条链的信息的题,可以考虑使用边分治;
边分治时的主要思想跟点分治一样,一直去找某个重心,把一棵树不断化成更小的部分,边分治需要找的这个重心在边上,使得去掉这条边过后两边剩的点的差最小,写法跟点分治都差不多,但是边分治会被菊花图这样的树给卡成\(n^2\),所以在边分治之前要重构原树,把原树变成一棵二叉树,具体做法就是新建虚节点然后把真实儿子放在虚二叉树的叶子节点上,这样做就没有数据能把边分治卡下来,空间参考堆式线段树的算法,变成了\(4n\)的样子;
边分治每次把当前联通快分成了两坨,计算贡献也比点分治方便多了;
以下边分治部分的代码自己yy的,可能写丑了或者傻逼自带大常数了;
例题 BZOJ#2870
/*
Name: BZOJ#2870
Author: CIao
Date: 09/03/19 17:08
Description: 边分治
*/
#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define RG register
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef long double LD;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
char c = getchar();
bool f = false;
for (x = 0; !isdigit(c); c = getchar()) {
if (c == '-') {
f = true;
}
}
for (; isdigit(c); c = getchar()) {
x = x * 10 + c - '0';
}
if (f) {
x = -x;
}
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
read(x), read(y...);
}
const int INF=65536+1,N=2e5+10;
int n,p=1,O,Ed,TOT,size,root,MAXV,TMP;
int head[N],A[N],V[N],num[N];
LL ANS;
bool G[N],No[N<<1];
vector<int> E[N];
struct Edge {
int to,last,w;
Edge () {}
Edge (int a,int b,int c) :to(a),last(b),w(c) {}
}edge[N<<1];
void ADD(int a,int b,int c) {
edge[++p]=Edge(b,head[a],c); head[a]=p;
edge[++p]=Edge(a,head[b],c); head[b]=p;
}
void Build(int l,int r,int f) {
if(l>r) return;
if(l==r) {
ADD(f,V[l],1);
return;
}
int mid=l+r>>1,rt=++n; A[rt]=A[f];
ADD(f,rt,0);
Build(l,mid,rt); Build(mid+1,r,rt);
}
void DFS(int u,int f) {
int cnt=0;
for(int i=0;i<(int)E[u].size();++i) if(E[u][i]!=f) V[++cnt]=E[u][i];
int mid=1+cnt>>1;
Build(1,mid,u); Build(mid+1,cnt,u);
for(int i=0;i<(int)E[u].size();++i) if(E[u][i]!=f) DFS(E[u][i],u);
}
void GetEdge(int u,int f) {
num[u]=1;
for(int i=head[u];i;i=edge[i].last) {
int v=edge[i].to;
if(!No[i]&&v!=f) {
GetEdge(v,u);
num[u]+=num[v];
}
}
for(int i=head[u];i;i=edge[i].last) {
int v=edge[i].to;
if(!No[i]&&v!=f) {
if(MAXV>abs(TOT-num[v]*2)) {
root=u; Ed=i;
MAXV=abs(TOT-num[v]*2);
}
}
}
}
int cnt,tot;
struct Data {
int val,dis;
}F[N],P[N];
void Getdis(int u,int f,int val,int dis) {
val=min(val,A[u]);
if(G[u]) {
F[++cnt]=(Data){val,dis+1};
ANS=max(ANS,1ll*(dis+1)*val);
}
for(int i=head[u];i;i=edge[i].last) {
int v=edge[i].to;
if(!No[i]&&v!=f) Getdis(v,u,val,dis+edge[i].w);
}
}
void Calc(int u,int f,int val,int dis) {
val=min(val,A[u]);
if(G[u]) P[++tot]=(Data){val,dis+TMP};
for(int i=head[u];i;i=edge[i].last) {
int v=edge[i].to;
if(!No[i]&&v!=f) Calc(v,u,val,dis+edge[i].w);
}
}
bool cmp(Data A,Data B) {
return A.val==B.val?A.dis<B.dis:A.val<B.val;
}
void Solve() {
if(!cnt||!tot) return;
sort(F+1,F+cnt+1,cmp); sort(P+1,P+tot+1,cmp);
for(int i=cnt-1;i;--i) F[i].dis=max(F[i].dis,F[i+1].dis);
int now=1;
for(int i=1;i<=tot;++i) {
while(now!=cnt&&F[now].val<P[i].val) ++now;
if(F[now].val>=P[i].val) ANS=max(ANS,1ll*P[i].val*(P[i].dis+F[now].dis));
}
}
void DAC(int u1,int e) {
if(e==-1) return;
int u2=edge[e].to; No[e]=No[e^1]=true; TMP=edge[e].w;
cnt=tot=0; Getdis(u1,0,INF,0); Calc(u2,0,INF,0); Solve();
cnt=tot=0; Getdis(u2,0,INF,0); Calc(u1,0,INF,0); Solve();
int S1=TOT-num[u2],S2=num[u2];
root=u1; Ed=-1; MAXV=S1+1; TOT=S1; GetEdge(u1,0); DAC(root,Ed);
root=u2; Ed=-1; MAXV=S2+1; TOT=S2; GetEdge(u2,0); DAC(root,Ed);
}
//#define rua
int main() {
// ios::sync_with_stdio(false);
#ifdef rua
#endif
read(n);
for(int i=1;i<=n;++i) read(A[i]),G[i]=true;
for(int i=1;i<n;++i) {
int u,v; read(u,v);
E[u].push_back(v); E[v].push_back(u);
}
DFS(1,0);
root=1; Ed=-1; MAXV=n+1; TOT=n; GetEdge(1,0);
DAC(root,Ed);
printf("%lld\n",ANS);
return 0;
}