zoukankan      html  css  js  c++  java
  • poj 1741 Tree 点分治

    题目链接:http://poj.org/problem?id=1741

    给你n个点,n-1条带权值边以及数字k。要你求树上所有dis(u,v)<=k的点对,(u,v)与(v,u)视作一个。

    对于这种树上路径问题我们可以采用树分治来进行处理,这里采用点分治来处理。

    要求出所有dis(u,v)<=k的点对我们可以先选一点作根,求出所有经过该点且满足距离小于等于k的路径,再分别在其子树中选择一点作根,求出所有过根且满足距离小于等于k的路径,就这样一直分治下去,我们就可以求得所有dis(u,v)<=k的点对。

    在点分治过程中,选点决定了点分治的复杂度,点选的不好复杂度跟O(n^2)没有差别,那么我们每次选点都是选重心,这样每次分治后树的节点数至多为分治前节点树的一半,递归的深度就是logn。在每次分治时O(n)找出所有点到根的距离,O(nlogn)快排并用双指针O(n)算出小于等于k的路径数。这样总的复杂度就是O(nlog2n)。

    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define maxn 100005
    #define inf 0x3f3f3f3f
    int n,k,cnt,root,ans,maxx,head[maxn],size[maxn],son[maxn],vis[maxn];
    struct edge{
        int to,next,val;
    }e[maxn];
    vector<int>dis;
    void add(int u,int v,int val)
    {
        e[++cnt].to=v;
        e[cnt].next=head[u];
        e[cnt].val=val;
        head[u]=cnt;
    }
    void dfs_size(int u,int fa)//求各点子树大小 
    {
        size[u]=1;son[u]=0;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v!=fa&&!vis[v])
            {
                dfs_size(v,u);
                size[u]+=size[v];
                son[u]=max(son[u],size[v]);
            }
        }
    }
    void dfs_root(int N,int u,int fa)//求重心 
    {
        son[u]=max(son[u],N-size[u]);
        if(maxx>son[u])
        {
            root=u;
            maxx=son[u];
        }
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v!=fa&&!vis[v])
            dfs_root(N,v,u);
        }
    }
    void dfs_dis(int u,int fa,int val)//求出所有点到根的距离 
    {
        dis.push_back(val);
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v!=fa&&!vis[v])
            dfs_dis(v,u,val+e[i].val);
        }
    }
    int cal(int u,int val)//计算小于等于k的路径数 
    {
        dis.clear();
        dfs_dis(u,0,val);
        sort(dis.begin(),dis.end());
        int l=0,r=dis.size()-1,ret=0;
        while(l<r)//two-pointer
        {
            while(dis[l]+dis[r]>k&&l<r)--r;
            ret+=r-l;
            l++;
        }
        return ret;
    }
    void dfs(int u)
    {
        dfs_size(u,0);
        maxx=inf;
        dfs_root(size[u],u,0);
        ans+=cal(root,0);//此时算出的路径数是包括没经过这个根的路径数,后面需要减去这种的路径数 
        vis[root]=1;//将选的roo又被遍历t标记,防止其在之后的分治过程中 
        for(int i=head[root];i;i=e[i].next)
        {
            int v=e[i].to,val=e[i].val;
            if(!vis[v])
            {
                ans-=cal(v,val);//子树所有边加上dis(u,v)后满足的路径数就是需要减去的路径数 
                dfs(v);//递归分治 
            }
        }
    }
    int main()
    {
        while(scanf("%d%d",&n,&k)&&(n+k))
        {
            int u,v,val;
            for(int i=1;i<=n;i++)
            head[i]=vis[i]=0;
            cnt=ans=0;
            for(int i=1;i<n;i++)
            {
                scanf("%d%d%d",&u,&v,&val);
                add(u,v,val);
                add(v,u,val);
            }
            dfs(1);
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    第八章:简单之美——布尔代数和搜索引擎的索引
    第六章:信息的度量和作用
    第五章:隐马尔可夫模型
    第四章谈谈中文分词
    第二章:自然语言处理———从规则到统计
    转:中文分词算法笔记
    NLTK之WordNet 接口【转】
    sentiwordnet的简单使用
    20169202 2016-2017-2 《移动平台开发实践》实验总结--七周
    20169202 2016-2017-2《移动平台》第七周学习总结
  • 原文地址:https://www.cnblogs.com/chen99/p/11580399.html
Copyright © 2011-2022 走看看