• POJ 1741 Tree (点分治)


                                                                        Tree
    Time Limit: 1000MS   Memory Limit: 30000K
    Total Submissions: 20816   Accepted: 6820

    Description

    Give a tree with n vertices,each edge has a length(positive integer less than 1001).
    Define dist(u,v)=The min distance between node u and v.
    Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
    Write a program that will count how many pairs which are valid for a given tree.

    Input

    The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
    The last test case is followed by two zeros.

    Output

    For each test case output the answer on a single line.

    Sample Input

    5 4
    1 2 3
    1 3 1
    1 4 2
    3 5 1
    0 0
    

    Sample Output

    8

    Source

    【分析】

    给一棵边带权树,问两点之间的距离小于等于K的点对有多少个。

    将无根树转化成有根树进行观察。满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中。

    如果我们已经知道了此时所有点到根的距离a[i],a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出。然后根据分治的思想,分别对所有的儿子求一遍即可,但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉(显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算)。

    在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心,这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)。

    树的重心的算法可以线性求解。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <time.h>
    #include <string>
    #include <map>
    #include <stack>
    #include <vector>
    #include <set>
    #include <queue>
    #define met(a,b) memset(a,b,sizeof a)
    #define pb push_back
    #define lson(x) ((x<<1))
    #define rson(x) ((x<<1)+1)
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    const int M=1e6+10;
    int n,m,k,tot,size,root,ans;
    int head[N],s[N],f[N],d[N];
    bool done[N];
    vector<int>dep;
    struct man{
        int to,next,l;
    }edg[N*2];
    void add(int u,int v,int l){
        edg[tot].to=v;edg[tot].l=l;edg[tot].next=head[u];head[u]=tot++;
    }
    void getroot(int u,int fa){
        s[u]=1;f[u]=0;
        for(int i=head[u];i!=-1;i=edg[i].next){
            int v=edg[i].to;
            if(v!=fa&&!done[v]){
                getroot(v,u);
                s[u]+=s[v];
                f[u]=max(f[u],s[v]);
            }
        }
        f[u]=max(f[u],size-s[u]);
        if(f[u]<f[root])root=u;
    }
    void getdep(int u,int fa){
        dep.push_back(d[u]);
        s[u]=1;
        for(int i=head[u];i!=-1;i=edg[i].next){
            int v=edg[i].to;
            if(v!=fa&&!done[v]){
                d[v]=d[u]+edg[i].l;
                getdep(v,u);
                s[u]+=s[v];
            }
        }
    }
    int calc(int u,int init){
        dep.clear();
        d[u]=init;
        getdep(u,0);
        sort(dep.begin(),dep.end());
        int ret=0;
        for(int l=0,r=dep.size()-1;l<r; ){
            if(dep[l]+dep[r]<=k)ret+=r-l++;
            else r--;
        }
        return ret;
    }
    void work(int u){
        ans+=calc(u,0);
        done[u]=true;
        for(int i=head[u];i!=-1;i=edg[i].next){
            int v=edg[i].to;
            if(!done[v]){
                ans-=calc(v,edg[i].l);
                f[0]=size=s[v];
                getroot(v,root=0);
                work(root);
            }
        }
    }
    int main(){
        while(~scanf("%d%d",&n,&k)&&n&&k){
            tot=ans=0;f[0]=size=n;
            met(head,-1);met(done,false);
            int u,v,l;
            for(int i=1;i<n;i++){
                scanf("%d%d%d",&u,&v,&l);
                add(u,v,l);add(v,u,l);
            }
            getroot(1,root=0);
            work(root);
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    unity 切换场景
    unity视频教程
    Unity3D的按钮添加事件有三种方式
    swift中实现cell中局部播放的动画效果
    swift中使用UIColllectionView实现横向轮播的一般方法
    swift中利用系统线程实现异步加载数据同步更新UI
    用UICollectionView实现上下轮播的案例
    Swift轮播控件快速入门——FSPagerView
    Swift
    Swift实现iOS录音与播放音频功能
  • 原文地址:https://www.cnblogs.com/jianrenfang/p/6432549.html
Copyright © 2020-2023  润新知