边分治学习笔记

6 篇文章 0 订阅

对于某些点分治不太好合并两条链的信息的题,可以考虑使用边分治;
边分治时的主要思想跟点分治一样,一直去找某个重心,把一棵树不断化成更小的部分,边分治需要找的这个重心在边上,使得去掉这条边过后两边剩的点的差最小,写法跟点分治都差不多,但是边分治会被菊花图这样的树给卡成 n 2 n^2 n2,所以在边分治之前要重构原树,把原树变成一棵二叉树,具体做法就是新建虚节点然后把真实儿子放在虚二叉树的叶子节点上,这样做就没有数据能把边分治卡下来,空间参考堆式线段树的算法,变成了 4 n 4n 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;
}

````对于某些点分治不太好合并两条链的信息的题,可以考虑使用边分治;
边分治时的主要思想跟点分治一样,一直去找某个重心,把一棵树不断化成更小的部分,边分治需要找的这个重心在边上,使得去掉这条边过后两边剩的点的差最小,写法跟点分治都差不多,但是边分治会被菊花图这样的树给卡成$n^2$,所以在边分治之前要重构原树,把原树变成一棵二叉树,具体做法就是新建虚节点然后把真实儿子放在虚二叉树的叶子节点上,这样做就没有数据能把边分治卡下来,空间参考堆式线段树的算法,变成了$4n$的样子;
边分治每次把当前联通快分成了两坨,计算贡献也比点分治方便多了;
以下边分治部分的代码自己yy的,可能写丑了或者傻逼自带大常数了;
#例题 BZOJ#2870
````cpp
/*
	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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值