鱼C论坛

 找回密码
 立即注册
查看: 2588|回复: 1

[技术交流] 050:Tree

[复制链接]
发表于 2018-2-19 02:04:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
Description

Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Input

The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
Output

For each test case output the answer on a single line.
Sample Input

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
Sample Output

8

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-2-19 02:05:23 | 显示全部楼层
理论参考https://wenku.baidu.com/view/8861df38376baf1ffc4fada8.html?re=view

树的分治

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>

  4. using namespace std;
  5. const int maxn=10010;
  6. int N,K;
  7. int root;        //根结点
  8. int ans;        //结果
  9. int Max;       
  10. struct node
  11. {
  12.         int v;
  13.         int next;
  14.         int w;
  15. }edge[maxn*2];        //无向图
  16. int tot;        //结点总数total
  17. int head[maxn];        //头结点数组head
  18. int size[maxn];        //以v为根的子树节点数size
  19. int maxv[maxn];       
  20. int vis[maxn];        //遍历标志visit
  21. int dis[maxn];        //距离数组distance
  22. int num;
  23. void init()  
  24. {  
  25.     tot=0;  
  26.     ans=0;  
  27.     memset(head,-1,sizeof(head));  
  28.     memset(vis,0,sizeof(vis));  
  29. }   
  30. //建立邻接表
  31. void add_edge(int u,int v,int w)
  32. {
  33.         edge[tot].v=v;
  34.         edge[tot].w=w;
  35.         edge[tot].next=head[u];//形成了链表,以-1结尾,以head为头结点,每个结点都是结点u的孩子。
  36.         head[u]=tot++;
  37. }
  38. //计算子树所含节点数
  39. void dfssize(int u,int f) //除去f点外的规模,f一般为父结点
  40. {
  41.         size[u]=1;        //本身 一个结点
  42.         maxv[u]=0;        //其最大子树的结点数
  43.         for(int i=head[u];i!=-1;i=edge[i].next)
  44.         {
  45.                 int v=edge[i].v;
  46.                 if(v==f||vis[v])
  47.                         continue;
  48.                 dfssize(v,u);
  49.                 size[u]+=size[v];
  50.                 if(size[v]>maxv[u])
  51.                         maxv[u]=size[v];
  52.         }
  53. }
  54. //确定树的重心,即最佳的根结点。
  55. void dfsroot(int r,int u,int f)        //r为根结点,u为比较结点
  56. {
  57.         if(size[r]-size[u]>maxv[u])        //size[r]-size[u]是u上面部分的树的尺寸,maxv[u]是u的最大子树的结点数。
  58.                 maxv[u]=size[r]-size[u];//此处将树看作二叉树,把最大子树作为一枝,剩余的看作一枝 。将最大的一枝存入maxv中
  59.         if(maxv[u]<Max)        Max=maxv[u],root=u;  //MAX是maxv中最小值,由于maxv必不小于总结点的一半 ,故MAX存储的是当前最平衡结点的最大枝个数
  60.         for(int i=head[u];i!=-1;i=edge[i].next)
  61.         {
  62.                 int v=edge[i].v;
  63.                 if(v==f||vis[v])
  64.                         continue;
  65.                 dfsroot(r,v,u);
  66.         }
  67. }
  68. //求每个点离重心的距离  
  69. void dfsdis(int u,int d,int f)  
  70. {
  71.         dis[num++]=d;
  72.         for(int i=head[u];i!=-1;i=edge[i].next)  
  73.     {  
  74.         int v=edge[i].v;  
  75.         if(v!=f&&!vis[v])  
  76.             dfsdis(v,d+edge[i].w,u);  
  77.     }  
  78. }
  79. //计算以u为根的子树中有多少点对的距离小于等于K  
  80. int calc(int u,int d)  //d为通向u的路径长度  
  81. {  
  82.     int ret=0;         //点对的个数
  83.     num=0;  //初始化
  84.     dfsdis(u,d,0);  
  85.     sort(dis,dis+num);  
  86.     int i=0,j=num-1;  //0点为根结点本身
  87.     while(i<j)  
  88.     {  
  89.         while(dis[i]+dis[j]>K&&i<j)j--;  
  90.         ret+=j-i;  
  91.         i++;  
  92.     }  
  93.     return ret;  
  94. }  
  95. void dfs(int u)
  96. {
  97.         Max=N;
  98.         dfssize(u,0);
  99.         dfsroot(u,u,0);        //设置u为根
  100.         ans+=calc(root,0);        //u树中的点对
  101.         vis[root]=1;        //根结点已遍历
  102.         for(int i=head[root];i!=-1;i=edge[i].next)  
  103.     {  
  104.         int v=edge[i].v;  
  105.         if(!vis[v])  
  106.         {  
  107.             ans-=calc(v,edge[i].w);  //删除掉同属于v结点且满足 dis[i]+dis[j]<K的点对。
  108.             dfs(v);  //加上以v为结点仅可过v结点满足 dis[i]+dis[j]<K的点对。
  109.         }  
  110.     }  
  111. }
  112. int main()
  113. {
  114.         while(scanf("%d%d",&N,&K)!=EOF)
  115.         {
  116.                 if(!N&&!K)
  117.                         break;
  118.                 int u,v,w;
  119.                 init();
  120.                 for(int i=1;i<N;i++)  
  121.         {  
  122.             scanf("%d%d%d",&u,&v,&w);  
  123.             add_edge(u,v,w);  
  124.             add_edge(v,u,w);  
  125.         }
  126.                 dfs(1);
  127.                 printf("%d\n",ans);
  128.         }
  129.         return 0;
  130. }
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-19 23:57

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表