Luogu P3806 【模板】点分治

前言:

本文简述点分治。


约定:

本文除标程外,均只讨论一次询问。


用一般dfs解决这个问题:

我们可以先预处理出所有可能的距离,则可以\(O(1)\)查询。
可以对每个节点\(i\)维护一个\(fd\)数组,表示以\(i\)为根的子树中,以\(i\)为端点的链的长度有哪些值能取到,最终时间复杂度为\(O(n^2)\),由于该题时限很短,无法通过。


点分治:

发现若一般dfs遇到一颗深度为\(O(\log n)\)的树,可以不使用bitset,转而使用普通数组,这样可以精确控制转移,可以只转移实际存在的链长度。那么时间复杂度十分优秀\(O(n\log n)\)
考虑能不能对普通的树进行转化,使得其变为深度为\(O(\log n)\)的树。
假设当前遍历到以\(root\)为根的子树,则可以将路径分为经过\(root\)和不经过\(root\),这是普通dfs的思想。
考虑处理完经过\(root\)的路径后,剩下的若干子树如何快速处理。
发现处理这些子树时,以哪个节点为根并不重要,所以可以直接以该子树的重心为根。
这样每次都选重心,则剩下的连通块大小会不断\(/2\),所以递归深度是\(O(\log n)\)的。
这样就达成了我们的目的——吗?

时间复杂度:

发现每次递归到一个节点,为了计算链长度,都需要重新遍历一遍整个子树,因为递归的时候不是向其儿子递归的。那么时间复杂度会有什么影响,考虑证明:
因为递归深度是\(O(\log n)\)的,而同一层递归到的子树一定是不交的,于是它们的大小之和是\(O(n)\)的,所以整体复杂度依旧为\(O(n\log n)\)


标程:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+5;
const int K = 1e7+5;
bool vis[N],fd[K],hs;
int siz[N],cq[N],cq2[N],cqtp2,cqtp,qtp,k,s;
int q[N][3],sn[3];
vector<pair<int,int> > edg[N];
void resiz(int now,int f){
	siz[now] = 1;
	for(auto& i : edg[now]){
		if(i.first==f||vis[i.first]) continue;
		resiz(i.first,now);
		siz[now]+=siz[i.first];
	}
}
int ask(int now,int f){
	int ans = 0;
	bool fg = true;
	for(auto& i : edg[now]){
		if(i.first==f||vis[i.first]) continue;
		ans = max(ans,ask(i.first,now));
		if(siz[i.first]>s/2) fg = false;
	}
	if(fg&&s-siz[now]<=s/2) return now;
	return ans;
}
void sl(int now){
	resiz(now,0);
	s = siz[now];
	now = ask(now,0);
	cqtp2 = 0,fd[0] = true;
	for(auto& i : edg[now]){
		if(vis[i.first]) continue;
		qtp = 0,cqtp = 0;
		q[++qtp][0] = i.first;
		q[qtp][1] = i.second;
		q[qtp][2] = now;
		while(qtp){
			sn[0] = q[qtp][0];
			sn[1] = q[qtp][1];
			sn[2] = q[qtp--][2];
			if(sn[1]>k) continue;
			cq[++cqtp] = sn[1];
			if(fd[k-sn[1]]){
				hs = true;
				for(;cqtp2;cqtp2--) fd[cq2[cqtp2]] = false;
				return;
			}
			for(auto& j : edg[sn[0]]){
				if(j.first==sn[2]||vis[j.first]) continue;
				q[++qtp][0] = j.first;
				q[qtp][1] = sn[1]+j.second;
				q[qtp][2] = sn[0];
			}
		}
		for(;cqtp;cqtp--){
			fd[cq[cqtp]] = true;
			cq2[++cqtp2] = cq[cqtp];
		}
	}
	for(;cqtp2;cqtp2--) fd[cq2[cqtp2]] = false;
	fd[0] = false;
	vis[now] = true;
	for(auto& i : edg[now]){
		if(vis[i.first]) continue;
		sl(i.first);
		if(hs) return;
	}
}
int main(){
	cin.tie(nullptr)->sync_with_stdio(false);
	int n,m;
	cin>>n>>m;
	for(int i=1,u,v,w;i<n;i++){
		cin>>u>>v>>w;
		edg[u].emplace_back(v,w);
		edg[v].emplace_back(u,w);
	}
	while(m--){
		hs = false;
		cin>>k;
		resiz(1,0);
		s = siz[1];
		for(int i=1;i<=n;i++) vis[i] = false;
		sl(ask(1,0));
		cout<<(hs?"AYE\n":"NAY\n");
	}
	
	return 0;
}
posted @ 2026-07-12 09:55  海因里奇  阅读(4)  评论(0)    收藏  举报