zoukankan      html  css  js  c++  java
  • [POJ 1741] Tree

    Link:

    POJ 1741 传送门

    Solution:

    此题的难点在于点分治上的统计

    如果依然采取每棵子树的结果与之前所有子树的结果合并的方式会比较麻烦

    同时复杂度可能超过$O(n*log(n))$的限制

    其实可以用一个简单的容斥来在$O(n*log(n))$统计结果:

    先将所有点到重心的距离排序,维护左右指针来直接统计结果

    但这样明显会重复计算两个点在同一棵子树中的情况,按照同样的操作将其除去即可

    Code:

    //by NewErA
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <queue>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    typedef pair<int,int> P;
    
    const int MAXN=10005;
    vector<P> G[MAXN];vector<int> tmp;
    int n,k,mx_sub[MAXN],sz[MAXN],vis[MAXN],v_sum=0,root,res=0;
    int dist[MAXN];
    
    void getroot(int x,int anc)
    {
        sz[x]=1;mx_sub[x]=0;
        for(int i=0;i<G[x].size();i++)
        {
            int v=G[x][i].first;
            if(v==anc || vis[v]) continue;
            getroot(v,x);sz[x]+=sz[v];
            mx_sub[x]=max(mx_sub[x],sz[v]);
        }
        mx_sub[x]=max(mx_sub[x],v_sum-sz[x]);
        if(mx_sub[x]<mx_sub[root]) root=x;
    }
    
    void cal_dep(int x,int anc)
    {
        tmp.push_back(dist[x]);
        for(int i=0;i<G[x].size();i++)
        {
            int v=G[x][i].first;
            if(v==anc || vis[v]) continue;
            dist[v]=dist[x]+G[x][i].second;
            cal_dep(v,x);
        }
    }
    
    int cal(int x,int start_point)
    {
        tmp.clear();dist[x]=start_point;
        cal_dep(x,0);sort(tmp.begin(),tmp.end());
        int ret=0,l=0,r=tmp.size()-1;
        while(l<r)
        {
            if(tmp[l]+tmp[r]<=k)
                ret+=(r-l),l++;
            else r--;
        }
        return ret;
    }
    
    void solve(int x)
    {
        res+=cal(x,0);vis[x]=true;
        for(int i=0;i<G[x].size();i++)
        {
            int v=G[x][i].first;
            if(vis[v]) continue;
            res-=cal(v,G[x][i].second);
            v_sum=sz[v];getroot(v,root=0);
            solve(root);
        }
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&k)!=EOF && n && k)
        {
            for(int i=0;i<=n;i++) G[i].clear(),vis[i]=mx_sub[i]=0;
            for(int i=1;i<n;i++)
            {
                int x,y,z;
                scanf("%d%d%d",&x,&y,&z);
                G[x].push_back(P(y,z));G[y].push_back(P(x,z));
            }
            mx_sub[0]=v_sum=n;getroot(1,root=0);
            res=0;solve(root);
            printf("%d
    ",res);
        }
        return 0;
    }

    Review:

    点分治中可用容斥的思想来统计

    先计算任意两点的结果,再减去两点在同一棵子树中的结果(重心转移后会重复计算)

  • 相关阅读:
    asp.net FckEditor配置
    您请求的报表需要更多信息...
    水晶报表中如何动态增加字段
    使用JavaMail发送SMTP认证的邮件给多个收信人
    vim中删除每行行尾的空格
    转载:STUN在SIP中的工作原理及过程
    转载 URL和URI的区别
    转载 Android深入浅出Binder机制
    链接静态库的时候,命令行中库和源文件的位置问题
    使用dumpbin来查看程序的依赖
  • 原文地址:https://www.cnblogs.com/newera/p/9303056.html
Copyright © 2011-2022 走看看