点分治 学习笔记

点分治是树上暴力的优化,可以让 \(O(n^2)\) (枚举树上任意两点)的时间复杂度降为 \(O(n \log{n})\)

简单证明一下时间复杂度:

每次都取树的重心进行递归,则每次递归子树大小减半,递归至多 \(\log{n}\) 层。每一层有 \(n\) 个点,故时间复杂度 \(O(n \log{n})\)

例题:P3806 【模板】点分治 1

朴素的想法是:枚举树上任意两点,用倍增在求 lca 的同时求出两点间路径长度。时间复杂度 \(O(n^2 \log{n})\)

考虑点分治优化暴力。

(咕)


#include<bits/stdc++.h>
// #define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n,m;
vector<int> E[10005],V[10005];
bool vis[10005];
int qu[104];


const int MAXN=1e7;
//
//void add(int p,int k)
//{
//	for(int i=p;i<=MAXN;i+=lowbit(i)) t[i]+=k;
//}
//
//int query1(int p)
//{
//	int ans=0;
//	for(int i=p;i>0;i-=lowbit(i)) ans+=t[i];
//	return ans;
//}
//
//int query(int l,int r)
//{
//	return query1(r)-query1(l-1);
//}

void addddd(int u,int v,int w) { E[u].push_back(v); V[u].push_back(w); }

int siz[10005];
bool can[10005];
// int O;

void dfs1(int p,int fa)
{
	// O++;
	siz[p]=1;
	for(int i=0;i<E[p].size();i++)
	{
		int to=E[p][i];
		if(to==fa||vis[to]) continue;
		
		dfs1(to,p);
		siz[p]+=siz[to];
	}
}

//abs(maxn_siz-min_siz) 's minnum

//(abs,p)

inline pair<int,int> minn(pair<int,int> x,pair<int,int> y)
{
	if(x.first<y.first) return x;
	else return y;
}

inline pair<int,int> dfs2(int p,int fa,const int maxn_siz)
{
//	int nw=n+1,x=p,up=maxn_siz-siz[p],down=0;
	
	// O++;
	pair<int,int> ans1=make_pair(MAXN,0);
	int maxn=0;
	for(int i=0;i<E[p].size();i++)
	{
		int to=E[p][i];
		if(to==fa||vis[to]) continue;
		
		ans1=minn(ans1,dfs2(to,p,maxn_siz));
		maxn=max(maxn,siz[to]);
//		up-=siz[to];
//		down+=siz[to];
	}
//	if((abs(maxn_siz-siz[p]-siz[p]))<100000)cout<<"p="<<p<<" abs="<<(abs(maxn_siz-siz[p]-siz[p]))<<"\n";
	return minn(ans1,make_pair(max(maxn,maxn_siz-siz[p]),p));
}

bool f[MAXN+10];
vector<int> v,v2;

void dfs3(int p,int fa,int dis)
{
	// O++;
	if(dis>MAXN) return; 
	v.push_back(dis);
	v2.push_back(dis);
//	f[dis]=1;
	
	for(int i=0;i<E[p].size();i++)
	{
		int to=E[p][i];
		int w=V[p][i];
		if(to==fa||vis[to]) continue;
//		v.push_back()
		dfs3(to,p,dis+w);
	}
}

void solve(int root)
{
//	O++;
	vis[root]=1;
	siz[root]=0;

    // cout<<root<<"\n";

    for(int i=0;i<E[root].size();i++)
    {
        int to=E[root][i];
        if(vis[to]) continue;

        dfs1(to,root);
        int to_root=dfs2(to,root,siz[to]).second;
        // cout<<to_root<<"\n";
        if(!(to_root<1||to_root>n))
        {
            solve(to_root);
        }
    }
	for(int i=0;i<v2.size();i++) f[v2[i]]=0;
	v2.clear();
	for(int i=0;i<E[root].size();i++)
	{
		int to=E[root][i];
		int w=V[root][i];
        // cout<<w<<"\n";
		if(vis[to]) continue;
		
		v.clear();
		dfs3(to,root,w);
		
		for(int k=1;k<=m;k++)
		{
			if(can[k]) continue;
			for(int j=0;j<v.size();j++) if((qu[k]<=MAXN&&qu[k]-v[j]>=0&&f[qu[k]-v[j]])||v[j]==qu[k]) can[k]=1;
		}
		for(int j=0;j<v.size();j++) f[v[j]]=1;

	}
	vis[root]=0;
	// return f[k];
}

signed main()
{
	//  freopen("P3806_8.in","r",stdin);
//	freopen("a.out","w",stdout);
	n=read();
	m=read();
//	k=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read(),w=read();
		addddd(u,v,w);
		addddd(v,u,w);
	}
	
    dfs1(1,0);
    int root=dfs2(1,0,n).second;

//     cout<<root<<"\n";
	for(int i=1;i<=m;i++)
	{
		qu[i]=read();
	}
	solve(root);
	for(int i=1;i<=m;i++)
	{
		if(qu[i]<=10000000&&can[i]) cout<<"AYE\n";
		else cout<<"NAY\n";
	}
	
//	cout<<O<<"\n";
	//mt19937_64 myrand(time(0));
	return 0;
}

posted @ 2025-07-07 14:19  Wy_x  阅读(11)  评论(0)    收藏  举报