zoukankan      html  css  js  c++  java
  • poj 1741 树的分治

    /*
    树的分治
    题意:求树上两点间的距离<=k的点对数目;
    因为n<=10000,暴搜一定会超时,所以很明显用的是树的点分治进行处理:点分治即为把树进行递归,分别对每个子树进行操作,
    然后把每个子树的情况综合起来,对于这道题目,首先找到树根(即树的重心),对于该树,统计dis[i]+dis[j]<=k的数量,
    将无根树转化成有根树进行观察。满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中。
    如果我们已经知道了此时所有点到根的距离a[i],a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出。然后根据分治的思想,
    分别对所有的儿子求一遍即可,但是这会出现重复的——当前情况下两个点位于一颗子树中,
    那么应该将其减掉(显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算,避免重复)。
    在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心,这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)。
    树的重心的算法可以线性求解。
    树的重心的算法可以用一个dfs求出,但是因为这题的n是不确定的,我就写了两个dfs来求
    */
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #define N  11000
    #define inf 0x3fffffff
    struct node{
    int u,v,w,next;
    }bian[N*4];
    int head[N],yong,num[N],ma,minn,m,nn,vis[N];
    void init() {
     yong=0;
     memset(head,-1,sizeof(head));
    }
    void addedge(int u,int v,int w) {
    bian[yong].u=u;
    bian[yong].v=v;
    bian[yong].w=w;
    bian[yong].next=head[u];
    head[u]=yong++;
    }
    int Max(int v,int vv) {
    return v>vv?v:vv;
    }
    void  dfs4(int u,int fa) {//求出n
      int i;
      nn++;
      for(i=head[u];i!=-1;i=bian[i].next) {
          int  v=bian[i].v;
        if(v!=fa&&!vis[v])
            dfs4(v,u);
      }
      return ;
    }
    void dfs1(int u,int fa) {//求重心
      num[u]=1;
      int i,tit=0;
      for(i=head[u];i!=-1;i=bian[i].next) {
        int v=bian[i].v;
        if(v!=fa&&!vis[v]) {
            dfs1(v,u);
            num[u]+=num[v];
            tit=Max(tit,num[v]);
        }
      }
      tit=Max(tit,nn-num[u]);
      if(tit<minn) {
        minn=tit;
        ma=u;
      }
      return ;
    }
    int diss[N],len;
    void  dfs3(int u,int fa,int w) {//统计重心到每个点的距离
        int i;
       // printf("sa%d  %d",u,w);
        diss[len++]=w;
        for(i=head[u];i!=-1;i=bian[i].next) {
            int v=bian[i].v;
            if(v!=fa&&!vis[v])//这里注意判断条件要加上!vis[v];
                dfs3(v,u,w+bian[i].w);
        }
        return ;
    }
    int cmp(const void *a,const void *b) {
    return *(int *)a-*(int *)b;
    }
    int dfs2(int u,int fa,int w) {//求出重心为u时的符合条件的个数
        len=0;
        dfs3(u,fa,w);
        qsort(diss,len,sizeof(diss[0]),cmp);
        int i,j,ans=0;
       // for(i=0;i<len;i++)
     //     printf("%d ",diss[i]);
       //     printf("
    ");
        for(i=0,j=len-1;i<j;i++) {
            while(i<j&&diss[i]+diss[j]>m)
                j--;
             ans+=j-i;
        }
        return ans;
    }
    int dfs(int u) {
      minn=inf;
      memset(num,0,sizeof(num));
      nn=0;
      dfs4(u,-1);//求n
      dfs1(u,-1);//求重心
     // printf("u=%d cou=%d
    ",u,nn);
      // printf("%d
    ",ma);
      vis[ma]=1;//将重心标记走过
      int ans=dfs2(ma,-1,0);//以ma为重心的符合条件的对数
      //printf("a%d
    ",ans);
      int i;
      for(i=head[ma];i!=-1;i=bian[i].next) {//这里注意从ma开始遍历子树
        int v=bian[i].v;
        if(!vis[v]) {
            ans-=dfs2(v,-1,bian[i].w);//将在一个子树上的情况减去,因为接下来会再算一遍,避免重复
            ans+=dfs(v);//计算子树,递归求和
        }
      }
      return ans;
    }
    int main() {
        int  i,j,k,ii,n;
        while(scanf("%d%d",&n,&m),n||m) {
                init();
           for(ii=1;ii<n;ii++) {
                scanf("%d%d%d",&i,&j,&k);
                addedge(i,j,k);
                addedge(j,i,k);
            }
            memset(vis,0,sizeof(vis));//
            k=dfs(1);
            printf("%d
    ",k);
        }
    return 0;}
    

  • 相关阅读:
    多线程008如何预防死锁
    多线程003volatile的可见性和禁止指令重排序怎么实现的
    多线程011线程池运行原理及复用原理
    多线程010为什么要使用线程池
    多线程009什么是守护线程
    多线程005创建线程有哪些方式
    多线程007描述一下线程安全活跃态问题,以及竞态条件
    多线程002ThreadLocal有哪些内存泄露问题,如何避免
    关于Tomcat的启动时机(精通Eclipse Web开发P40)
    乱侃~~~
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410599.html
Copyright © 2011-2022 走看看