【学习笔记】点分治

P3086
点分治可以将O(n^2)优化成O(nlogn)

核心操作

每次把树存入,找出重心作为root,动态遍历子树每个点到root距离,再判断前面的子树是否可以与当前子树结合=k。给root打上重心标记记作删除,然后回退清空

复杂度

共log层,每层遍历整张图,复杂度O(nlogn)

代码

#include<bits/stdc++.h>
#define Pair pair<int,int>
#define w first
#define to second
using namespace std;
const int N=1e4+10,Q=1e7+10;int n,m;
bool aaa;
vector<Pair> mp[N];
int ask[N];int ans[N];
int cnt;
int del[N];//是否被删除
int siz[N];
int mson[N];
void fsiz(int u,int fa){
	cnt++;
	siz[u]=1;
	for(auto e:mp[u]){
		int v=e.to;
		if(v==fa||del[v]) continue;
		fsiz(v,u);
		siz[u]+=siz[v];
	}
}
int root;
void Root(int u,int fa){
	mson[u]=cnt-siz[u];
	for(auto e:mp[u]){
		int v=e.to;
		if(v==fa||del[v]) continue;
		Root(v,u);	
		mson[u]=max(mson[u],siz[v]);	
	}
	if(root==-1||mson[u]<mson[root]) root=u;
}
bool hav[Q];//数值是否存在
vector<int> nown;
void get(int u,int fa,int dis){//计算当前子树数值
	nown.push_back(dis);
	for(auto e:mp[u]){
		int v=e.to,w=e.w;
		if(v==fa||del[v]) continue;
		get(v,u,dis+w);
	}
}
void solve(int u){
	//获取重心
	cnt=0;fsiz(u,u);
	root=-1;Root(u,u);
	//子树处理
	hav[0]=1;
	u=root;
	for(auto e:mp[u]){
		int v=e.to,w=e.w;
		if(del[v]) continue;
		int beg=nown.size();
		get(v,u,w);
		for(int i=1;i<=m;i++){
			for(int j=beg;j<nown.size()&&(!ans[i]);j++){
				if(ask[i]>=nown[j]) ans[i]|=hav[ask[i]-nown[j]];
			}
		}
		for(int i=beg;i<nown.size();i++){
			if(nown[i]<Q) hav[nown[i]]=1;
		}
	}
	//回退
	while(nown.size()){
		if(nown.back()<Q)hav[nown.back()]=0;
		nown.pop_back();
	}
	del[u]=1;
	for(auto e:mp[u]){
		int v=e.to;
		if(del[v]) continue;
		solve(v);
	}
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<n;i++){
		int a,b,c;cin>>a>>b>>c;
		mp[a].push_back({c,b});
		mp[b].push_back({c,a});
	}
	for(int i=1;i<=m;i++) cin>>ask[i];
	solve(1);
	for(int i=1;i<=m;i++) cout<<(ans[i]?"AYE\n":"NAY\n");
	return 0;
}

代码易错点(警示后人)

  • 第15行,cnt++
  • 第51行,u=root
posted @ 2025-12-04 18:38  Ming3398  阅读(1)  评论(0)    收藏  举报