zoukankan      html  css  js  c++  java
  • POJ 1741

      它这道题的分类是分治,但我还是觉得和树的关系更密切些。

      也算是完成了第一个最简单的部分——基 本 算 法(更强大的在后面)。

      是近来打过的比较难的题目了,因为涉及到很多新的东西

      也花了挺长时间去学点分治和树的重心之类的东西。

      首先,O(n^2)的爆搜还是很容易想到的吧,可惜过不了。

      考虑无根树转有根树然后点分治。

      首先把它转成有根树,那么所有的点对就被分成两类:经过根的和不经过根的,我们可以讨论一下

      1.经过根的点对:只需要DFS得出它们距离根的距离dis[x],dis[y],判断它们的和是否小于k

      2.不经过根的点对:那么就分治下去,计算经过根的子树的根的点对(也分两种情况考虑)

      最后需要注意的是有些点在经过根时被计算了一次,在子树中又被重复计算了多次,需要减去

      在计算点对多少时,可以先排序,再用 two points 扫一次即可

      如果根节点随意挑选那么很容易退化成链,所以每次找根节点都要找树(或子树)的重心。

      然后就是一些细节问题了,具体看代码

      CODE

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=10005;
    struct edge
    {
        int to,next,v;
    }e[N*2];
    int head[N],t,n,m,i,x,y,z,k,size[N],f[N],dis[N],ans,sum,tot,root;
    bool vis[N];
    inline void read(int &x)
    {
        x=0; char ch=getchar();
        while (ch<'0'||ch>'9') ch=getchar();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    }
    inline void write(int x)
    {
        if (x/10) write(x/10);
        putchar(x%10+'0');
    }
    inline void add(int x,int y,int z)
    {
        e[++k].to=y; e[k].v=z; e[k].next=head[x]; head[x]=k;
    }
    inline int max(int a,int b)
    {
        return a>b?a:b;
    }
    inline void getroot(int now,int fa)
    {
        size[now]=1; f[now]=0;
        for (int i=head[now];i!=-1;i=e[i].next)
        if (e[i].to!=fa&&!vis[e[i].to])
        {
            getroot(e[i].to,now);
            size[now]+=size[e[i].to];
            f[now]=max(f[now],size[e[i].to]);
        }
        f[now]=max(f[now],sum-size[now]);
        if (f[now]<f[root]) root=now;
    }
    inline void DFS(int now,int fa,int d)
    {
        dis[++tot]=d;
        for (int i=head[now];i!=-1;i=e[i].next)
        if (e[i].to!=fa&&!vis[e[i].to]) DFS(e[i].to,now,d+e[i].v);
    }
    inline int work(int now,int d)
    {
        tot=0;
        DFS(now,-1,d);
        sort(dis+1,dis+tot+1);
        int l=1,r=tot,res=0;
        while (l<r)
        {
            while (dis[l]+dis[r]>m&&l<r) --r;
            res+=r-l++;
        }
        return res;
    }
    inline void solve(int now)
    {
        root=0; f[0]=1e9;
        getroot(now,-1);
        vis[root]=1;
        ans+=work(root,0);
        for (int i=head[root];i!=-1;i=e[i].next)
        if (!vis[e[i].to])
        {
            ans-=work(e[i].to,e[i].v);
            sum=size[e[i].to];
            solve(e[i].to);
        }
    }
    int main()
    {
        for (;;)
        {
            k=ans=0; 
            memset(head,-1,sizeof(head));
            memset(e,-1,sizeof(e));
            memset(vis,0,sizeof(vis));
            read(n); read(m); sum=n;
            if (n==0&&m==0) break;
            for (i=1;i<n;++i)
            {
                read(x); read(y); read(z);
                add(x,y,z); add(y,x,z);
            }
            solve(1);
            write(ans); putchar('
    ');
        }
        return 0;
    }
  • 相关阅读:
    【leetcode_easy_greedy】1403. Minimum Subsequence in Non-Increasing Order
    【leetcode_easy_sort】1030. Matrix Cells in Distance Order
    word2010 标题编号
    WORD2013中如何使用多级列表自动编号(转)
    Javaweb权限管理设计思路 (转)
    前后端分离模式下的权限设计方案 (转)
    Axure rp8.1.0.3381 3382 激活码(2019-07-26 亲测可用) 转
    通达OA开发网址
    C# Winform 跨线程更新UI控件常用方法汇总 (转)
    C#中的Invoke
  • 原文地址:https://www.cnblogs.com/cjjsb/p/8479613.html
Copyright © 2011-2022 走看看