Processing math: 100%

点分治学习笔记

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

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

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

来看几道题目:

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

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

hdu4812D-Tree
类似于第一题,此题就是求权值dy满足dx×dyk(mod 1e6+3)
显然 dyk×invdx(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 @   Isenthalpic  阅读(54)  评论(0编辑  收藏  举报
编辑推荐:
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
阅读排行:
· 本地部署 DeepSeek:小白也能轻松搞定!
· 基于DeepSeek R1 满血版大模型的个人知识库,回答都源自对你专属文件的深度学习。
· 在缓慢中沉淀,在挑战中重生!2024个人总结!
· 大人,时代变了! 赶快把自有业务的本地AI“模型”训练起来!
· Tinyfox 简易教程-1:Hello World!
点击右上角即可分享
微信分享提示