Tree(树分治入门)
题目链接:http://poj.org/problem?id=1741
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 35091 | Accepted: 11718 |
Description
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 last test case is followed by two zeros.
Output
Sample Input
5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0
Sample Output
8
Source
题意:一棵有n个节点的树,每条边有个权值代表相邻2个点的距离,要求求出所有距离不超过k的点对(u,v)
题解:树分治
假设树以root为根节点,那么满足要求的点对有2种情况:
①路径经过root且dis(u,v)<=k
②路径不经过root,即其路径的最高点为子树上某一节点
对于第②种情况可以通过递归求解,这里只讨论第一种情况
该如何求解路径经过root且dis(u,v)<=k的合法点对数呢?
设dir[u]为u到根节点root的距离,那么只有满足dir[u]+dir[v]<=k且LCA(u,v)==root的点对才是合法的,
设cnt1=树中所有dis(u,v)<=k的点对数,cnt2=LCA(u,v)==root的子节点的合法点对数
那么以root为根的树种合法点对数为:ans=cnt1-cnt2
找出有多少个dir[u]+dir[v]的方法很简单:只需要排序后扫一遍即可。
总结一下算法的过程:
①计算以u为根的树种每棵子树的大小
②根据子树大小找出树的重心root(以树的重心为根的树,可以使其根的子树中节点最多的子树的节点最少)
③以root为根,计算树中每个点到root的距离dir
④计算树中所有满足dir[u]+dir[v]<=k的点对数cnt1
⑤计算以root的子节点为根的子树中,满足dir[u]+dir[v]<=k的点对数cnt2
⑥ans+=cnt1-cnt2
注意:每次计算完cnt1后,要将vis[root]=1,这样就可以将一棵树分解成若干棵子树
看代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<vector> using namespace std; typedef long long LL; const int maxn=2e4+5; const int INF=1e9+7; int cnt=0; int head[maxn<<1]; int sonnum[maxn<<1],sonmax[maxn<<1]; int mi,pos; int N,K; int sum=0; bool vis[maxn<<1]; LL ans; vector<int>dis; struct Edge { int next,to,w; }e[maxn<<1]; void Init() { ans=0;cnt=0; for(int i=0;i<maxn;i++) { head[i]=-1;sonnum[i]=sonmax[i]=0; vis[i]=false; } } void add_edge(int u,int v,int w) { e[++cnt].to=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; } void Query_size(int root,int pre)//求当前树的子树大小 { sum++;//存树的大小 sonnum[root]=1;sonmax[root]=0;//注意初始化 for(int i=head[root];i!=-1;i=e[i].next) { int v=e[i].to; if(vis[v]||v==pre) continue; Query_size(v,root); sonnum[root]+=sonnum[v];//子树节点有多少个 sonmax[root]=max(sonmax[root],sonnum[v]);//最大的子树节点个数 } } void Query_root(int root,int pre,int sum)//求当前树的重心 { for(int i=head[root];i!=-1;i=e[i].next) { int v=e[i].to; if(vis[v]||v==pre) continue; Query_root(v,root,sum); } int ma=max(sonmax[root],sum-sonnum[root]); if(mi>ma) { mi=ma;pos=root; } } void Query_dis(int root,int pre,int d) { dis.push_back(d); for(int i=head[root];i!=-1;i=e[i].next) { int v=e[i].to,w=e[i].w; if(vis[v]||v==pre) continue; Query_dis(v,root,d+w); } } int cal(int root,int d) { int ret=0; dis.clear();//存所有子节点到本身的距离 Query_dis(root,0,d); sort(dis.begin(),dis.end()); int i=0,j=dis.size()-1; while(i<j) { while(i<j&&dis[i]+dis[j]>K) j--; ret+=j-i; i++; } return ret; } void dfs(int root,int pre) { sum=0; mi=INF; Query_size(root,pre); Query_root(root,pre,sum);// int rt=pos; ans+=cal(rt,0);//pos为找到的重心 vis[rt]=true;//一定要标记 否则会往回走 for(int i=head[rt];i!=-1;i=e[i].next) { int v=e[i].to,w=e[i].w; if(vis[v]) continue; ans-=cal(v,w); dfs(v,rt); } } int main() { while(scanf("%d%d",&N,&K)!=EOF) { Init(); if(N==0&&K==0) break; for(int i=1;i<N;i++) { int u,v,w;scanf("%d%d%d",&u,&v,&w); add_edge(u,v,w); add_edge(v,u,w); } dfs(1,0); printf("%lld\n",ans); } return 0; }
原文地址:https://www.cnblogs.com/caijiaming/p/11569226.html
- memcpy的函数
- 你的密码被破解了?看看你的Apple ID、邮箱密码是不是这些!
- Oozie分布式任务的工作流——脚本篇
- hexo配置自己的博客站点
- Oozie分布式任务的工作流——Sqoop篇
- realloc invalid pointer错误解析
- qsort(),sort()排序函数
- 前端打包成桌面应用、以及chrome扩展
- Oozie分布式任务的工作流——Spark篇
- vue 实现 tomato timer(蕃茄钟)
- Linux下TCP连接过程总结
- PHP常用库函数介绍+常见疑难问题解答
- AcFun×讯飞输入法AI方言保护计划 用方言打破次元壁
- directshow、 Emgucv入门
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 为什么要用TypeScript
- np.clip截取函数
- 常见编程模式之双指针
- python操作txt文件中数据教程[2]-python提取txt文件中的行列元素
- JSON 是什么?它能带来什么?它和 XML 比较?
- 一起来学演化计算-实数空间变异算子
- 卡特兰数入门
- 常见编程模式之动态规划:0-1背包问题
- stat 命令家族(2)- 详解 pidstat
- MTO和MaTO MMZDT
- stat 命令家族(3)- 详解 mpstat
- 知识图谱入门(一)
- PHP判断变量内容是什么编码(gbk?utf-8) mb_detect_encoding
- stat 命令家族(4)- 详解 iostat
- PHP将数组存入数据库中的四种方式