poj1741 分类: poj 2015-02...

poj1741

题意:找出图中距离不大于k的点对的数目


解法:

将无根树转化为有根树

首先可以想到枚举,时间复杂度O(N2),效率很稳定


挖掘题目的信息,发现
满足条件的点对有两种情况:

1>两个点的路径经过树根
2>两个点位于同一颗子树中。

又因为:dist(rootx)+dist(rooty)<=kdist排序后可以求出满足该条件的点对数目,时间复杂度为O(sizelog2size)


所以路径经过root的点对数目:count(root)=find(root)find(son)

这样就可以在O(PNlogN)(P为结点最多被访问次数)时间内解决这个问题,然而P的值很不稳定,对于特殊形态的树 (例如 链)运行效率很低

于是采用点分治,分治复杂度O(NlogN),可以使P<=logN

时间总复杂度O(N(logN)2)

参考:http://blog.sina.com.cn/s/blog_6d5aa19a0100o73m.html

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<time.h>
#define MAXN  10005
#define MAXN2 20005

int n,k;
struct edge{int v,w,next;}mp[MAXN2];
int head[MAXN],ml;
int ba[MAXN],s[MAXN];
int bowl[MAXN],l;
int flag[MAXN];
int ans,swap;

void newedge(int u0,int v0,int w0)
{
    ++ml;

    mp[ml].v=v0;
    mp[ml].w=w0;
    mp[ml].next=head[u0];
    head[u0]=ml;

}
inline void dfsroot(int a,int fa)
{
    int i,p;

    bowl[++l]=a;

    s[a]=1,ba[a]=0;

    for(i=head[a];i;i=mp[i].next)
      if((p=mp[i].v)!=fa && !flag[p])
    {
        dfsroot(p,a);

        s[a]+=s[p];

        if(s[p]>ba[a])
          ba[a]=s[p];
    }
}
inline void dfsbowl(int a,int len,int fa)
{
    int i,p;

    bowl[++l]=len;

    for(i=head[a];i;i=mp[i].next)
      if((p=mp[i].v)!=fa && !flag[p])
    {
        dfsbowl(p,len+mp[i].w,a);
    }
}
inline int getroot(int a)
{
   int i,p,q,ret;   

   l=0;

   dfsroot(a,0); 

   ret=bowl[1];
   for(i=1;i<=l;i++)
   {
      p=bowl[i],q=s[a]-s[p];

      if(q>ba[p])ba[p]=q;

      if(ba[p]<ba[ret])ret=p;
    }

    return ret;
}



inline void sort(int a,int b)
{
    int i=a,j=b;
    int t=bowl[rand()%(b-a+1)+a];

    while(i<=j)
    {
        while(bowl[i]<t)i++;
        while(bowl[j]>t)j--;

        if(i<=j)
        {
            swap=bowl[i];
            bowl[i]=bowl[j];
            bowl[j]=swap;

            i++,j--;
        }

    }

    if(a<j)sort(a,j);
    if(i<b)sort(i,b);

}

inline int calculate(int x,int l0)
{
    int i,j,ret=0;

    l=0;dfsbowl(x,l0,0);

    if(l>1)sort(1,l);

    for(i=1,j=l;i<=l && j>i;i++)
    {
        while(bowl[i]+bowl[j]>k && j>i)j--;

        ret+=j-i;
    }

    return ret;
}

inline void work(int a)
{
    int i,p,root=getroot(a);

    flag[root]=1;

    ans+=calculate(root,0);

    for(i=head[root];i;i=mp[i].next)
      if(!flag[p=mp[i].v])
    {
        ans-=calculate(p,mp[i].w);
    }

    for(i=head[root];i;i=mp[i].next)
      if(!flag[p=mp[i].v])
    {
        work(p);
    }
}

int main()
{
    int i,a,b,c;

    #ifndef ONLINE_JUDGE
    freopen("poj1741.in","r",stdin);
    freopen("Poj1741.out","w",stdout);
    #endif

    while(1)
    {
        scanf("%d%d",&n,&k);

        if(!n && !k)break;

        ans=ml=0;
        memset(head,0,sizeof(head));
        memset(flag,0,sizeof(flag));

        for(i=1;i<n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            newedge(a,b,c);newedge(b,a,c);
        }

        work(n);

        printf("%d\n",ans);

    }

    #ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    #endif
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/dashgua/p/4723117.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值