码迷,mamicode.com
首页 > 其他好文 > 详细

动态点分治复习

时间:2020-04-28 10:07:46      阅读:55      评论:0      收藏:0      [点我收藏+]

标签:树结构   算法思路   注意   关于   log   cpp   代码   关系   区分   

动态点分治复习

算法思路

大概就是让一个点管辖一堆点,并构成父子关系,以维护一些东西。发现使重心为管辖点最好。点分树有几个性质,树高logn,可以暴力爬树维护。并且注意这里的父子关系可能在原树上离得很远,要注意区分。

关于写法

写两棵树的全局变量非常麻烦,可以封装结构体。注意有时候tree1和tree2要相互调用,可以这样写:

int dis(int,int);
struct tree1{
    ...
    void add(int u,int v){
        ...
    }
    void modify(int u,int w){
        ... dis(u,fa[now]) ...
    }
}t1;
struct tree2{
    ...
    void get_dis(int a,int b){
        ...
    }
    void dfs(int now){
        ... t1.add(fa[now],now); ...
    }
}t2;
int dis(int a,int b){
    return t2.get_dis(a,b);
}

这样调试会方便一点,因为可以在两个结构体里开一样的变量名。

核心代码

	inline void getrt(int u,int pre){
		sz[u]=1,mx[u]=0;//注意每次都要初始化 
		for(int i=head[u];i;i=edge[i].nex){
			int v=edge[i].v;
			if(vis[v]||v==pre) continue;
			getrt(v,u);sz[u]+=sz[v];
			mx[u]=max(mx[u],sz[v]);
		}
		mx[u]=max(mx[u],sum-sz[u]);
		if(mx[u]<mx[rt]) rt=u;
	}
	inline void work(int u,int pre){
		vis[u]=1;fa[u]=pre;
		for(int i=head[u];i;i=edge[i].nex){
			int v=edge[i].v;
			if(vis[v]) continue;
			sum=sz[v];mx[0]=sz[v];rt=0;
			getrt(v,0);t2.add(u,rt,v);
			work(rt,u);
		}
	}

做题思路

维护查询的东西,跟点分治差不多。注意树结构不变。修改,考虑对一些管辖点维护的东西的变化,可以发现这些点就是点分树上到根的路径上的点,暴力爬树修改即可。

举个例子:

长度为k的链权值之和

用动态开点线段树每个点维护经过这个点的长度为k的链长之和,把每个点可以接上的长度为k的链有多少个存起来(总状态数\(O(n\log n)\)),每次爬树用线段树区间修改(注意减去重复的部分),复杂度\(n\log^2n\)。注意树上联通块大小每次至少减半,于是线段树总共的待修改区间是\(O(n)\)的,这部分常数很小(只要你范围写对)。

动态点分治复习

标签:树结构   算法思路   注意   关于   log   cpp   代码   关系   区分   

原文地址:https://www.cnblogs.com/lcyfrog/p/12791715.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!