• POJ 1741 树上 点的 分治


    题意就是求树上距离小于等于K的点对有多少个

    n2的算法肯定不行,因为1W个点

    这就需要分治。可以看09年漆子超的论文

    本题用到的是关于点的分治。

    一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。

    每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心

    找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。

    最后的复杂度是n logn logn    其中每次快排是nlogn 而递归的深度是logn 

    友情链接:~~http://blog.csdn.net/sdj222555/article/details/7893862 

      1 #include <iostream>  
      2 #include <algorithm>  
      3 #include <cstring>  
      4 #include <string>  
      5 #include <cstdio>  
      6 #include <cmath>  
      7 #include <queue>  
      8 #include <map>  
      9 #include <set>  
     10 #define eps 1e-5  
     11 #define MAXN 11111  
     12 #define MAXM 55555  
     13 #define INF 1000000000  
     14 using namespace std;  
     15 struct EDGE  
     16 {  
     17     int v, next, w;  
     18 }edge[MAXM];  
     19 int head[MAXN], e;  
     20 int n, k, vis[MAXN], ans, root, num;  
     21 void init()  // 清空初始值
     22 {  
     23     memset(vis,0,sizeof(vis));  
     24     memset(head,-1,sizeof(head));  
     25     e=ans=0;  
     26 }  
     27 void add(int u,int v,int w)  // 边表  加边
     28 {  
     29     edge[e].v=v;  
     30     edge[e].w=w;  
     31     edge[e].next=head[u];  
     32     head[u]=e++;  
     33 }  
     34 int mx[MAXN], size[MAXN], mi, dis[MAXN];  
     35 void dfssize(int u, int fa) //处理以u为顶的子树的大小  fa是其父节点
     36 {  
     37     size[u] = 1;  
     38     mx[u] = 0;  
     39     for(int i = head[u]; i != -1; i = edge[i].next)  
     40     {  
     41         int v = edge[i].v;  
     42         if(v != fa && !vis[v])  
     43         {  
     44             dfssize(v, u);  
     45             size[u] += size[v];  
     46             if(size[v] > mx[u]) mx[u] = size[v];  
     47         }  
     48     }  
     49 }  
     50 void dfsroot(int r,int u,int fa)//求重心所谓重心是指删去该点后
     51 {  //所形成的子树的节点数最大的最小
     52     if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];  
     53     if(mx[u]<mi) mi=mx[u],root=u;  
     54     for(int i=head[u];i!=-1;i=edge[i].next)  
     55     {  
     56         int v=edge[i].v;  
     57         if(v!=fa&&!vis[v]) dfsroot(r,v,u);  
     58     }  
     59 }  
     60 void dfsdis(int u, int d, int fa) //求所有点到达重心的距离  即dis
     61 {  
     62     dis[num++] = d;  
     63     for(int i = head[u]; i != -1; i = edge[i].next)  
     64     {  
     65         int v = edge[i].v;  
     66         if(v != fa && !vis[v]) dfsdis(v, d + edge[i].w, u);  
     67     }  
     68 }  
     69 int calc(int u,int d)  
     70 {  
     71     int ret=0;  
     72     num=0;  
     73     dfsdis(u,d,0);  
     74     sort(dis,dis+num);  
     75     int i=0,j=num-1;  
     76     while(i<j) //经典  
     77     {  
     78         while(dis[i]+dis[j] > k && i < j) j--;  
     79         ret+=j-i;  
     80         i++;  
     81     }  
     82     return ret;  
     83 }  
     84 void dfs(int u)  
     85 {  
     86     mi = n;  
     87     dfssize(u, 0);  // 子树大小
     88     dfsroot(u, u, 0);  // 重心  求完之后  root 即为重心
     89     ans+=calc(root,0);//经过root的并且满足要求的点对数(这时候会出现重边)
     90     vis[root]=1;  
     91     for(int i = head[root]; i != -1; i = edge[i].next)  
     92     {  
     93         int v=edge[i].v;  
     94         if(!vis[v])  
     95         {  
     96             ans-=calc(v,edge[i].w);//v是root的son 以v,edge[i].w继续向下深搜
     97             //若这样还是满足要求(经过son并且满足要求的点对数,
     98             //这就是重边的情况,这时将它减掉)
     99             dfs(v);//继续处理root的son,情况同上  
    100         }  
    101     }  
    102 }  
    103 int main()  
    104 {  
    105     while(scanf("%d%d", &n, &k) != EOF)  
    106     {  
    107         if(!n && !k) break;  // 不到终止条件
    108         init();  
    109         int u, v, w;  
    110         for(int i = 0; i < n - 1; i++)  
    111         {  
    112             scanf("%d%d%d", &u, &v, &w);  
    113             add(u, v, w);  
    114             add(v, u, w);  
    115         }  
    116         dfs(1);  
    117         printf("%d
    ", ans);  
    118     }  
    119     return 0;  
    120 }

    附上图解:(图是手绘的,见谅)

    当一遍处理的时候 ,root等于1,这时候ans会把5-1-2这样的点对加进去,所以我们以2,dis[2](dis[2]=1)再深搜,若是再这样的条件下还满足条件(过2节点,【此时dis值没变】,还满足小于等于7,说明他就是重边,应该减掉,而对于2-5这样合法的会在dfs[2]的时候,处理好)

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<algorithm>
     5 using namespace std;
     6 #define MAXN 11111
     7 #define MAXM 55555
     8 struct EDGE{
     9     int v,w,next;
    10 }edge[MAXM]; 
    11 int n,k,vis[MAXN],mx[MAXN],e,ans;
    12 int root,num,dis[MAXN],head[MAXN],size[MAXN],mi;
    13 void init(){
    14     memset(vis,0,sizeof(vis));
    15     memset(head,-1,sizeof(head));
    16     ans=e=0;
    17 }
    18 void add(int u,int v,int w){
    19     edge[e].v=v;edge[e].w=w;
    20     edge[e].next=head[u];head[u]=e++;
    21 }
    22 void dfssize(int u,int fa){
    23     size[u]=1;mx[u]=0;
    24     for(int i=head[u];i!=-1;i=edge[i].next){
    25         int v=edge[i].v;
    26         if(v!=fa&&!vis[v]){
    27             dfssize(v,u);
    28             size[u]+=size[v];
    29             if(size[v]>mx[u]) mx[u]=size[v];
    30         }
    31     }
    32 }
    33 void dfsroot(int r,int u,int fa){
    34     if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];
    35     if(mx[u]<mi){ mi=mx[u];root=u; }
    36     for(int i=head[u];i!=-1;i=edge[i].next){
    37         int v=edge[i].v;
    38         if(v!=fa&&!vis[v]){
    39             dfsroot(r,v,u);
    40         }
    41     }
    42 }
    43 void dfsdis(int u,int d,int fa){
    44     dis[num++]=d;
    45     for(int i=head[u];i!=-1;i=edge[i].next){
    46         int v=edge[i].v;
    47         if(v!=fa&&!vis[v]){
    48             dfsdis(v,d+edge[i].w,u);
    49         }
    50     }
    51 }
    52 int calc(int u,int d){
    53     int ret=0;
    54     num=0;
    55     dfsdis(u,d,0);sort(dis,dis+num);
    56     int i=0,j=num-1;
    57     while(i<j){
    58         while(dis[i]+dis[j]>k&&i<j) j--;
    59         ret+=j-i;
    60         i++;
    61     }
    62     return ret;
    63 }
    64 void dfs(int u)
    65 {
    66     mi=n;
    67     dfssize(u,0);dfsroot(u,u,0);
    68     ans+=calc(root,0);
    69     vis[root]=1;
    70     for(int i=head[root];i!=-1;i=edge[i].next){
    71         int v=edge[i].v;
    72         if(!vis[v]){
    73             ans-=calc(v,edge[i].w);
    74             dfs(v);
    75         }
    76     }
    77 }
    78 int main()
    79 {
    80     while(scanf("%d%d",&n,&k)!=EOF)
    81     {
    82         if(!n&&!k) break;
    83         init();int u,v,w;
    84         for(int i=1;i<n;i++){
    85             scanf("%d%d%d",&u,&v,&w);
    86             add(u,v,w);add(v,u,w);
    87         }
    88         dfs(1);
    89         printf("%d
    ",ans);
    90     }
    91     return 0;
    92 }

    另附网址:09年漆子超论文  提取密码:95tu

  • 相关阅读:
    pycharm中启动Django方法
    Python ——selenium报错 'chromedriver.exe' executable needs to be in PATH
    软件测试
    C#&.Net干货分享- 构建PrinterHelper直接调用打印机相关操作
    C#&.Net干货分享- iTextSharp导出数据源到PDF
    C#&.Net干货分享-构建Aocr_ImageHelper读取图片文字做解析
    C#&.Net干货分享-构建后台自动定时任务的源码
    SQL Server清理数据库日志的脚本-干货
    SQL Server通过函数把逗号分隔的字符串拆分成数据列表的脚本-干货
    SQL Server通过定义函数返回字段数据列表模板-干货
  • 原文地址:https://www.cnblogs.com/suishiguang/p/6139299.html
Copyright © 2020-2023  润新知