首页 > 其他分享 >树分治

树分治

时间:2024-01-31 21:44:35浏览次数:28  
标签:rt 路径 siz ll 分治 maxp

点分治

适合处理大规模的树上路径信息。

实现

取一个中心点计算跨过中心点的贡献。(lsy说得精辟但抽象)

先随便指定一个根 \(rt\),我们能将树上的路径分为经过 \(rt\) 的路径包含于 \(rt\) 的某棵子树内的路径(不经过 \(rt\))

对于经过当前根节点的路径,又可以分为两种,一种是以根节点为一个端点的路径,另一种是两个端点都不为根节点的路径。而后者又可以由两条属于前者的链合并得到。

接着我们枚举 \(rt\),先计算在其子树中且经过该节点的路径对答案的贡献,再递归其子树对不经过该节点的路径进行求解。

OI-wiki说着有点抽象,其实就是不断地在上一个中心点的子树中去取一个新中心点,然后一直去求解某一类路径,就是一个分治的思想。

然后随便找一个中心点的话如果树是一条链这个分治就会退化为 \(O(n^{\mathbf 2})\),所以我们要让递归层数尽量少,管的什么证明反正就是取树的重心。重心定义不再赘述,写下求法:记录 \(maxp[u]\) 表示删掉 \(u\) 之后产生的最大子树大小,重心就是 \(maxp\) 最小的点。下面是求法:

点击查看代码
void find(ll u,ll fa){
	siz[u]=1,maxp[u]=0;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(v==fa||vis[v]) continue;
		find(v,u);
		siz[u]+=siz[v];
		maxp[u]=max(maxp[u],siz[v]);
	}
	maxp[u]=max(maxp[u],sums-siz[u]); //sums是会变的子树总大小,会在分治过程中变为siz[v]
	if(maxp[u]<maxp[rt]) rt=u;
}

结合具体题目来分治。

P3806 【模板】点分治 1

首先离线询问省得每次都搞一遍淀粉质,然后先找到重心然后开始分治。每次求答案就把重心子树内的点到重心的距离求出来,然后枚举询问看有没有点的距离满足询问,这里具体是这样判的:开一个数组 \(flag_i\) 表示有没有点到重心的距离为 \(i\),if(q[k]>=dist[j]&&!ans[k]) ans[k]=fl[q[k]-dist[j]];

然后我们要记得清空这个 \(flag\) 数组,于是考虑开一个桶记录哪些 \(i\) 修改过。但是这里有些细节问题,\(dis[j]\) 可能太大了就会爆掉桶,所以要特判一下。

还有一些细节:我们每一次重新找重心都是先设为 \(\mathbf 0\),然后树总的大小就变成了 \(siz[v]\),因为我们是进入 \(v\) 的子树来分治的;一开始 \(flag_{\mathbf{0}}\) 就要设成 \(\mathbf 1\),毕竟也可能一个点和中心的距离直接就是询问值;然后数组开大,还有点细节看代码吧。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=114514,M=10000005; //注意数据范围 
struct xx{
	ll next,to,val;
}e[2*N];
ll head[2*N],cnt;
void add(ll x,ll y,ll z){
	e[++cnt].next=head[x];
	e[cnt].to=y;
	e[cnt].val=z;
	head[x]=cnt;
}
ll n,m,q[N],rt,sums;
ll siz[N],dis[N],maxp[N];
bool vis[N],fl[M],ans[M];
ll bu[M],tot;
void find(ll u,ll fa){
	siz[u]=1,maxp[u]=0;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(v==fa||vis[v]) continue;
		find(v,u);
		siz[u]+=siz[v];
		maxp[u]=max(maxp[u],siz[v]);
	}
	maxp[u]=max(maxp[u],sums-siz[u]);
	if(maxp[u]<maxp[rt]) rt=u;
}
ll res,dist[N];
void dfs_dis(ll u,ll fa){
	dist[++res]=dis[u];
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to,w=e[i].val;
		if(v==fa||vis[v]) continue;
		dis[v]=dis[u]+w;
		dfs_dis(v,u);
	}
}
void calc(ll u){
	tot=0;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to,w=e[i].val;
		if(vis[v]) continue;
		res=0,dis[v]=w;
		dfs_dis(v,u);
		for(int j=1;j<=res;++j) //枚举点 
			for(int k=1;k<=m;++k)//枚举询问 
				if(q[k]>=dist[j]&&!ans[k]) ans[k]=fl[q[k]-dist[j]];
		for(int j=1;j<=res;++j)
			if(dist[j]<=1e7) bu[++tot]=dist[j],fl[dist[j]]=1;
	}
	for(int i=1;i<=tot;++i) fl[bu[i]]=0;
}
void solve(ll u){
	vis[u]=1;
	calc(u);
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(vis[v]) continue;
		rt=0,maxp[rt]=N,sums=siz[v];
		find(v,0),solve(rt);
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m; sums=n;
	for(int i=1;i<n;++i){
		ll a,b,c;
		cin>>a>>b>>c;
		add(a,b,c),add(b,a,c);
	}
	for(int i=1;i<=m;++i) cin>>q[i];
	rt=0,sums=maxp[rt]=n,fl[0]=1;
	find(1,0),solve(rt);
	for(int i=1;i<=m;++i)
		if(ans[i]) cout<<"AYE\n";
		else cout<<"NAY\n";
	return 0;
}

鸽了

标签:rt,路径,siz,ll,分治,maxp
From: https://www.cnblogs.com/heshuwan/p/18000182

相关文章

  • 树分治相关
    树分治相关一种特别的分治思想,但难点不在于点分治思想本身。有板子,但是板子跟题目重点几乎无关。点分治淀粉质用途:用于处理树上多对点询问或寻找有条件最远(最近)点对。主要是处理多对点对。做法:我们先选择一个节点作为根节点\(rt\),所有完全位于其子树中的路径可以分为两......
  • 上辈子推的"分治 NTT"复杂度分析
    hdu7381Cargo式子部分由liuhangxin想出。\[\sum\limits_{i=0}^{n}\binom{k}{i}(n-i)^{k-i}[x^i]\prod\limits_{id=1}^{m}(1-x^{c_{id}})\]实现部分当时胡了一个分治NTT,也不知道时间复杂度为什么是对的,但是过了。AC后十多分钟分析出来这个做法的时间复杂度为\(......
  • 树分治学习笔记
    点分治0.用处点分治一般用于树上路径的问题。比如求条数等。1.点分治过程选择一个根节点计算贡献,贡献一般有一下两种1.两个点的路径经过根节点2.两个点在同一个子树内然后把根节点删掉,分成若干棵树,对每一棵树做同样的操作然后每一次我们只需要计算两个点的路......
  • 边分治和边分树
    边分治就是,每次选择一条边作为分治中心。然后把这条边断掉,在两个连通块内继续递归。考虑将原树三度化,就是对于\(u\)的每条出边,新建一个点\(w\),连边\((u,w,0),(w,v,d)\),然后令\(u=w\)。三度化后边分治的复杂度就是对的,为\(O(n\logn)\)。边分治的好处是,每次只用考......
  • Ybt 金牌导航 6.1.H. 时空旅行 / P5416 [CTSC2016] 时空旅行(线段树分治+凸包)
    题意简述初始有版本\(0\),其中仅包含点\(0\),且\(c_0\)给出,\(x_0=0\)。对于第\(i\)个版本,它依赖第\(fr_i\)个版本,而且会在父级版本的基础上进行以下两种操作之一:插入一个新点,并且会给出\(x_i\)和\(c_i\)。删除一个本就存在的点(不为\(0\))给出\(m\)次询问,每次给出......
  • CF-1921-F-根号分治
    1921-F题目大意有一个长为\(n\)的序列\(a\),有\(q\)次询问,对于每次询问:给定\(s,d,k\),请输出\(\sum_{i=1}^{k}i*a_{s+(i-1)*d}\)Solution根号分治。对于\(d\ge\sqrt{n}\)的情况,直接暴力计算即可。对于\(d\le\sqrt{n}\)的情况,这时需要预处理两个数组:\(pre,sum\),这里\(pr......
  • CEOI2023D1T3(LOJ4019) Brought Down the Grading Server? (分治+欧拉回路)
    因为我们有\(S=2^k\),所以我们先考虑\(k=1\)即\(S=2\)的时候应该怎么做。发现如果我们对于每一个核心从\(t_1\)向\(t_2\)连一条无向边,如果我们把「不交换\(t_1,t_2\)」看成将这条边定向为\(t_1\tot_2\),否则如果「交换\(t_1,t_2\)」看成将这条边定向为\(t_2\tot_1......
  • 根号分治
    原本准备讲题用的,结果讲急了没用上。乱写的,就扔这儿吧。先看一个题。CF797EArrayQueries给定长度为\(n\)的序列\(a\)。\(m\)次询问。每次询问给出\(p,k\)。您要不断地执行操作\(p\getsp+a_p+k\),直到\(p>n\)为止。询问的答案为操作次数。\(1\len,q\le10^5\),\(......
  • 一本通金牌导航 分治法 E.工程划分 / P5290 [十二省联考 2019] 春节十二响(启发式合并)
    题目传送门题意简述:将树上\(n\)个点划分为若干个集合,使得集合中的点两两没有祖孙关系。一个集合的权值是集合内点的权值的最大值,求所有集合的权值之和的最小值。首先这题有个非常显然的贪心:将几个权值大的点尽可能的合并到一个集合中是更优的。集合中的点两两没有祖孙关系,说......
  • GYM102596L Yosupo's Algorithm【分治,支配对】
    给定平面上\(2n\)个点,每个点有坐标\((x_i,y_i)\),权值\(w_i\)及颜色\(c_i\)。所有点满足:若\(c_i=0\),则\(x_i<0\);若\(c_i=1\),则\(x_i>0\)。\(q\)次查询,每次给定\(L_i,R_i\),你需要选择两个点\(i,j\)满足如下条件:\(c_i=0,c_j=1\)。\(x_i<L,x_j>R\)或\(x_......