动态点分治复习

动态点分治复习

算法思路

大概就是让一个点管辖一堆点,并构成父子关系,以维护一些东西。发现使重心为管辖点最好。点分树有几个性质,树高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(nlog n))),每次爬树用线段树区间修改(注意减去重复的部分),复杂度(nlog^2n)。注意树上联通块大小每次至少减半,于是线段树总共的待修改区间是(O(n))的,这部分常数很小(只要你范围写对)。

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