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

点分治

时间:2019-05-03 18:16:52      阅读:112      评论:0      收藏:0      [点我收藏+]

标签:lin   +=   --   log   style   表示   logs   turn   def   

点分治 讲解

点分治是一种树上分治思想 可以用更高的效率解决树上路径问题

以poj 1741 tree 为例子

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define ll long long int
using namespace std;
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
int moth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int dir[4][2]={1,0 ,0,1 ,-1,0 ,0,-1};
int dirs[8][2]={1,0 ,0,1 ,-1,0 ,0,-1, -1,-1 ,-1,1 ,1,-1 ,1,1};
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
int head[10007],vis[10007],d[10007];
struct node{
    int to,v,next;
};
node edge[10007<<1];
int cnt,ans,n,k;
void init(){
    cnt=0;
    ans=0;
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
}
void add(int from,int to,int val){
    edge[++cnt].v=val;
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt;
}
int son[10007]; //儿子节点数 
int now_size;  //子树的最小节点数,用于找重心 
int sz;   //当前操作树的节点数 
int root; //当前根节点 
void find_root(int u,int fa){ //找树重心 
    son[u]=1; int res=0;
    for(int i=head[u];i;i=edge[i].next){
        if(vis[edge[i].to]||edge[i].to==fa) continue;
        int to=edge[i].to;
        find_root(to,u);
        son[u]+=son[to];
        res=max(res,son[to]);
    }
    res=max(res,sz-son[u]);
    if(res<now_size) now_size=res,root=u;
}
int a[10007]; //临时储存路径值 
int tot;
void get_dis(int u,int fa){ //计算d数组 d[i]表示i点距离当前根节点的距离 
    a[++tot]=d[u];
    for(int i=head[u];i;i=edge[i].next){
        if(vis[edge[i].to]||edge[i].to==fa) continue;
        int to=edge[i].to;
        d[to]=d[u]+edge[i].v;
        get_dis(to,u);
    }
}
int solve(int u,int dis){ //找符合情况的个数 
    d[u]=dis; tot=0;
    get_dis(u,u);
    sort(a+1,a+1+tot);
    int l=1; int r=tot; int res=0;
    for(;l<r;++l){
        while(l<r&&a[l]+a[r]>k) --r;
        if(l<r) res+=(r-l);
    }
    return res;
}
void dfs(int u){ //分治 
    vis[u]=1;
    ans+=solve(u,0);
    int totsz=sz;
    for(int i=head[u];i;i=edge[i].next){
        if(vis[edge[i].to]) continue;
        int to=edge[i].to;
        ans-=solve(to,edge[i].v); //容斥思想 
        now_size=inf; root=0; 
        sz=son[to]>son[u]?totsz-son[u]:son[to];
        find_root(to,0);
        dfs(root);
    }
}
int main(){
    ios::sync_with_stdio(false);
    while(cin>>n>>k){
        if(!n&&!k) break;
        init();
        for(int i=1;i<n;i++){
            int from,to,val;
            cin>>from>>to>>val;
            add(from,to,val); add(to,from,val);
        }
        now_size=inf,sz=n,root=0;
        find_root(1,0); //找重心 
        dfs(root);
        cout<<ans<<endl;
    }
    return 0;
}

 

点分治

标签:lin   +=   --   log   style   表示   logs   turn   def   

原文地址:https://www.cnblogs.com/wmj6/p/10805725.html

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