点分治

Posted bennettz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点分治相关的知识,希望对你有一定的参考价值。

预备知识

  树的重心:删去这个点后,森林中所有树节点的最大值最小

点分治过程

具体实现

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 10005
struct Edge{
    int next,to,w;
}edge[maxn*2];
int fi[maxn],se,n,k,size[maxn],root,ma[maxn],sum,ans,depth[maxn];
int b[maxn],si;
bool vis[maxn];
inline void add_edge(int u,int v,int w){
    edge[++se].next=fi[u],edge[se].to=v,edge[se].w=w,fi[u]=se,
    edge[++se].next=fi[v],edge[se].to=u,edge[se].w=w,fi[v]=se;
}
int cal(int x){
    
}
void getroot(int x,int f){//找到当前树的重心 
    size[x]=1,ma[x]=0;
    for(int i=fi[x];i;i=edge[i].next){
        int v=edge[i].to;
        if(v==f||vis[v])continue;//vis[v]=1表示不属于本棵树 
        getroot(v,x);
        size[x]+=size[v];ma[x]=max(ma[x],size[v]);//ma[x]为按x分开后最大子树的节点树 
    }
    ma[x]=max(ma[x],sum-size[x]);//sum-size[x]为父节点连接的子树的大小 
    if(ma[x]<ma[root])root=x;//更新重心 
}
void solve(int x){
    ans+=cal(x);vis[x]=1;//先统计过重心的答案 
    for(int i=fi[x];i;i=edge[i].next){
        int v=edge[i].to;
        if(vis[v])continue;
        sum=size[v];root=0;
        getroot(v,0);//递归找子树的重心
        solve(root);//统计子树的答案 
    }
}
int main(){
    sum = ma[0] = n;//初始化 
    root = 0;
    getroot(1,0);//找重心 
    solve(root);//点分治 
    return 0;
}

例题

POJ 1741 同BZOJ 1468(下面为POJ1741代码)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 10005
struct Edge{
	int next,to,w;
}edge[maxn*2];
int fi[maxn],se,n,k,size[maxn],root,ma[maxn],sum,ans,depth[maxn];
int b[maxn],si;
bool vis[maxn];
inline void add_edge(int u,int v,int w){
	edge[++se].next=fi[u],edge[se].to=v,edge[se].w=w,fi[u]=se,
	edge[++se].next=fi[v],edge[se].to=u,edge[se].w=w,fi[v]=se;
}
void getroot(int x,int f){//找到当前树的重心 
	size[x]=1,ma[x]=0;
	for(int i=fi[x];i;i=edge[i].next){
		int v=edge[i].to;
		if(v==f||vis[v])continue;//vis[v]=1表示不属于本棵树 
		getroot(v,x);
		size[x]+=size[v];ma[x]=max(ma[x],size[v]);//ma[x]为按x分开后最大子树的节点树 
	}
	ma[x]=max(ma[x],sum-size[x]);//sum-size[x]为父节点连接的子树的大小 
	if(ma[x]<ma[root])root=x;//更新重心 
}
void getdeep(int x,int f){
	b[si++]=depth[x];
	for(int i=fi[x];i;i=edge[i].next){
		int v=edge[i].to;
		if(v==f||vis[v])continue;
		depth[v]=edge[i].w+depth[x];getdeep(v,x);
	}
}
int cal(int x,int s){
	depth[x]=s,si=0;
	getdeep(x,0);
	sort(b,b+si);
	int l=0,r=si-1,ans=0;
	while(l<r){
		if(b[l]+b[r]<=k){
			ans+=r-l;l++;
		}
		else r--;
	}
	return ans;
}
void solve(int x){
	ans+=cal(x,0);vis[x]=1;//先统计过重心的答案 
	for(int i=fi[x];i;i=edge[i].next){
		int v=edge[i].to;
		if(vis[v])continue;
		ans-=cal(v,edge[i].w);//本题在统计答案时有重复,这里去重 
		sum=size[v];root=0;
		getroot(v,0);//递归找子树的重心
		solve(root);//统计子树的答案 
	}
}
void work(){
	int u,v,w;memset(fi,0,sizeof(fi)),memset(vis,0,sizeof(vis));
	root=ans=se=0,sum=ma[0]=n;
	for(int i=1;i<n;i++)scanf("%d%d%d",&u,&v,&w),add_edge(u,v,w);
	getroot(1,0);
	solve(root);
	printf("%d\\n",ans);
} 
int main(){
	while(~scanf("%d%d",&n,&k)&&n|k)work();
	return 0;
}

 

以上是关于点分治的主要内容,如果未能解决你的问题,请参考以下文章

基于点分治的树分治

POJ 1741 Tree ——点分治

bzoj2599 [ IOI2011] -- 点分治

算法有关点分治的一些理解与看法

POJ 3714 分治/求平面最近点对

分治——最近点对