zoukankan      html  css  js  c++  java
  • 点分治

    预备知识

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

    点分治过程

    具体实现

    #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
    ",ans);
    } 
    int main(){
    	while(~scanf("%d%d",&n,&k)&&n|k)work();
    	return 0;
    }
    
  • 相关阅读:
    vijos 1894 セチの祈り
    luogu p1378 经验之谈
    審視自己
    高斯消去法的相關拓展
    通用汇点
    重征之战
    有文化的人吟了一句诗
    2016年7月总结
    BZOJ 1026: [SCOI2009]windy数
    BZOJ 1047: [HAOI2007]理想的正方形
  • 原文地址:https://www.cnblogs.com/bennettz/p/8902392.html
Copyright © 2011-2022 走看看