码迷,mamicode.com
首页 > 其他好文 > 详细

点分治

时间:2018-04-21 19:27:59      阅读:145      评论:0      收藏:0      [点我收藏+]

标签:void   can   com   bzoj   while   clu   tin   for   点分治   

预备知识

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

点分治过程

技术分享图片

具体实现

#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;
}

 

点分治

标签:void   can   com   bzoj   while   clu   tin   for   点分治   

原文地址:https://www.cnblogs.com/bennettz/p/8902392.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!