点分治学习笔记

点分治是一种对静态树上路径进行统计的一种思想。
它的的流程如下:

  1. 任意选取一个点为根节点。
  2. 算出它到当前这棵树所有点的距离\(d_i\)(也可以是其他的信息,视情况而定)。
  3. 那么对于没有重合路径(这里的路径定义为到根节点的路径)两个点的距离可以直接合并,(比如\(d_x+d_y\))。
  4. 去掉当前节点,树会分裂成若干个子树,再分治下去对子树做同样的过程。

在随机情况下,会比朴素算法快一点,
但如果是树一条链的话,递归深度达到\(n\)层,依旧很慢。
我们想想,每次应该选择什么点作为当前树的根节点呢?
其实就是树的重心
因为树的重心有一个性质:以重心为根后大小最大的子树不超过整棵树的一半。
所以至多递归\(logn\)层。

来看几道题目:

POJ1741Tree
对于每一个\(d_x\),统计有多少个\(d_y\)满足\(d_x+d_y\leqslant k\)
移下项:\(d_y \leqslant k - d_x\);
等式右边是个定值,很显然可以用树状数组维护,时间复杂度\(O(nlog^2n)\)
还有种做法是先将所有子树中的\(d_x\)算出来,排序,然后跑个双指针,大家可以自行百度题解。

luoguP3806
你可以使用上题的双指针,也可以使用二分。
鉴于本题值域较小,最简单当然是用个桶了。

hdu4812D-Tree
类似于第一题,此题就是求权值\(d_y\)满足\(d_x \times d_y \equiv k (mod~1e6+3)\)
显然 \(d_y \equiv k \times inv_{d_x}(mod~1e6+3)\)
预处理一下逆元,然后照着做就好了。。

总结:静态点分治较为简单,用简单的数据结构套个分治模板就行,关键在于发现维护的数据。

模板

#include<bits/stdc++.h>
#define inf (1<<30)
using namespace std;
const int N=2e5+10;
int n,T,cnt,ns,rt;
int pre[N],now[N],to[N],val[N],tot;
int me[N],siz[N],Q[N]; 
bool vis[N],can[N];
struct Node{
	int t,d,bl;
}a[N];
void add(int x,int y,int z){
	pre[++tot]=now[x];
	now[x]=tot;to[tot]=y;
	val[tot]=z;
}
void findrt(int u,int F){
	me[u]=0;siz[u]=1;
	for(int i=now[u];i;i=pre[i]){
		int v=to[i];
		if(v==F||vis[v])continue;
		findrt(v,u); 
		siz[u]+=siz[v];
		me[u]=max(me[u],siz[v]);
	}
	me[u]=max(me[u],ns-siz[u]);
	if(me[u]<me[rt])rt=u;
}
void DFS(int u,int F,int dis,int col){
	++cnt;
	a[cnt].t=u;
	a[cnt].d=dis;
	a[cnt].bl=col;
	for(int i=now[u];i;i=pre[i]){
		int v=to[i],z=val[i];
		if(vis[v]||v==F)continue;
		DFS(v,u,dis+z,col);
	}
}
bool cmp(Node A,Node B){
	if(A.d!=B.d)return A.d<B.d;
}
void solve(int u){
	cnt=0;
	++cnt;
	a[cnt].t=u;
	a[cnt].d=0;
	a[cnt].bl=u;
	for(int i=now[u];i;i=pre[i]){
		int v=to[i],z=val[i];
		if(vis[v])continue;
		DFS(v,u,z,v);
	}
	sort(a+1,a+cnt+1,cmp);
	for(int i=1;i<=T;i++){
		int l=1,r=cnt;
		while(l<r){
			if(a[l].d+a[r].d>Q[i])r--;
			else if(a[l].d+a[r].d<Q[i])l++;
			else if(a[r].bl==a[l].bl){
				if(a[r].bl==a[r-1].bl)r--;
				else l++;
			}
			else{
				can[i]=true;
				break;
			}
		}
	}
	return; 
}
void dfs(int u){
	vis[u]=true;
	solve(u);
	for(int i=now[u];i;i=pre[i]){
		int v=to[i];
		if(vis[v])continue;
		ns=siz[v];rt=0;
		findrt(v,0);
		dfs(rt);
	}
}
int main(){
	scanf("%d%d",&n,&T);
	for(int i=1;i<n;i++){
		int u,v,l;
		scanf("%d%d%d",&u,&v,&l);
		add(u,v,l);add(v,u,l);
	}
	for(int i=1;i<=T;i++)
		scanf("%d",&Q[i]);
	ns=n;me[0]=inf;rt=0;
	findrt(1,0);
	dfs(rt);
	for(int i=1;i<=T;i++)
		puts(can[i]?"AYE":"NAY");
	return 0;
}
posted @ 2021-02-08 22:02  Isenthalpic  阅读(53)  评论(0编辑  收藏  举报