POJ1741 Tree
2016-01-24 20:34
267 查看
描述:
给定一颗n个节点的带边权的树(边权不超过1001)
定义dist(u,v)为节点u与节点v之间的最小距离。
给定一整数k,当且仅当dist(u,v)不超过k,那个点对(u,v)就是合法的。
写一程序计算给定树有多少个合法点对。
输入:
输入包含多组测试样例。每组测试样例第一行为整数n,k(n<=10000)。接下来n-1行包含三个整数u,v,l。表示节点u与节点v之间有一条权值为l的边相连接。
输出:
对每组测试样例,输出答案。
分析:
楼教主的男人八题之一,经典树分治。
首先,路径可以根据他所经过的最久远的祖先来分类,以此为依据分在不同类里的路径没有重合。
我们考虑一条路径,他要么经过根节点,要么不经过(但是这种情况他会经过一个子树的根节点)。不经过的情况可以通过dfs的方式考虑。现只考虑经过根节点的情况。
我们一dist[i]表示某点到根节点(也可能是子树的)的距离。那么满足条件的路径的起点和终点应满足dist[i]+dist[j]<=K,同是i和j还不能在同一个孩子节点表示的子树里,否则他会在考虑相应子树的情况时被重复计数。这样每一个节点可以先加上所有的路径(包括重复的),再减去重复的路径。对于重复的路径,只要考虑其孩子节点所表示的子树,减去使之dist[i]+dist[j]<=K的情况(注意这时的dist仍是表示相对于原根节点的距离)。对于这种情况,我们可以将其转化为问题:已知一数列A[1],A[2],A[3]......A[m],求满足A[i]+A[j]<=K的元素对的数目。具体求解过程看代码,可以在O(n)时间内解决。这样对某一节点的路径计数就完成了。接下来只要把所有节点的路径都加起来就是答案了。
另外还要注意一点,当树是一条链时,将要考虑n层,这是时间复杂度退化到O(n^2)。于是我们可以每次找子树的重心(即用该点分割原树,所获得的森林里最大的树节点数最小的节点)。这样可以保证只考虑O(lgn)层,一共的时间复杂度是O(nlg^2n)。
代码:
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
#define max(x,y) ((x)<(y)?(y):(x))
int ans,dist
,K,n,siz
,dp
,Size,root;
树分治+求重心,我也算是八分之一的男人啦哈哈哈哈哈O(∩_∩)O~~
给定一颗n个节点的带边权的树(边权不超过1001)
定义dist(u,v)为节点u与节点v之间的最小距离。
给定一整数k,当且仅当dist(u,v)不超过k,那个点对(u,v)就是合法的。
写一程序计算给定树有多少个合法点对。
输入:
输入包含多组测试样例。每组测试样例第一行为整数n,k(n<=10000)。接下来n-1行包含三个整数u,v,l。表示节点u与节点v之间有一条权值为l的边相连接。
输出:
对每组测试样例,输出答案。
分析:
楼教主的男人八题之一,经典树分治。
首先,路径可以根据他所经过的最久远的祖先来分类,以此为依据分在不同类里的路径没有重合。
我们考虑一条路径,他要么经过根节点,要么不经过(但是这种情况他会经过一个子树的根节点)。不经过的情况可以通过dfs的方式考虑。现只考虑经过根节点的情况。
我们一dist[i]表示某点到根节点(也可能是子树的)的距离。那么满足条件的路径的起点和终点应满足dist[i]+dist[j]<=K,同是i和j还不能在同一个孩子节点表示的子树里,否则他会在考虑相应子树的情况时被重复计数。这样每一个节点可以先加上所有的路径(包括重复的),再减去重复的路径。对于重复的路径,只要考虑其孩子节点所表示的子树,减去使之dist[i]+dist[j]<=K的情况(注意这时的dist仍是表示相对于原根节点的距离)。对于这种情况,我们可以将其转化为问题:已知一数列A[1],A[2],A[3]......A[m],求满足A[i]+A[j]<=K的元素对的数目。具体求解过程看代码,可以在O(n)时间内解决。这样对某一节点的路径计数就完成了。接下来只要把所有节点的路径都加起来就是答案了。
另外还要注意一点,当树是一条链时,将要考虑n层,这是时间复杂度退化到O(n^2)。于是我们可以每次找子树的重心(即用该点分割原树,所获得的森林里最大的树节点数最小的节点)。这样可以保证只考虑O(lgn)层,一共的时间复杂度是O(nlg^2n)。
代码:
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
#define max(x,y) ((x)<(y)?(y):(x))
int ans,dist
,K,n,siz
,dp
,Size,root;
/*siz[i]表示以i为根节点的子树中节点的总数,dp[i]表示用节点i分割子树所获取的森林中节点数最大的子树里的节点数*/
/*Size表示所考虑的相应子树里节点的总数,这三个概念在求树的重心时会用到,root表示重心*/ bool done ;//表示某节点的路径属是否已被考虑,他也是分割原树的标志 struct Node{ int v,w; Node(int v,int w):v(v),w(w){} }; vector<Node> G ; vector<int> d; //记录子树下所有节点的dist void getRoot(int u,int fa){ //获取u所在子树的重心,fa为u的父节点,防止死循环 siz[u]=1;dp[u]=0;int v; //初始化siz[u]=1,即加上根节点 for(unsigned int i=0;i<G[u].size();++i){ if((v=G[u][i].v)==fa||done[v]) continue; //注意done[v]为真的点不能考虑,实际上就是在这里将不同的子树分割开来的 getRoot(v,u);siz[u]+=siz[v];dp[u]=max(dp[u],siz[v]); //向下求解,然后更新siz[u],dp[u] } dp[u]=max(Size-siz[u],dp[u]); //考虑u的父节点所在的树,这也是被分割出的森林的一部分 if(dp[root]>dp[u]) root=u; //更新root } void getDist(int u,int fa){ //获取各节点距根节点距离 siz[u]=1;int v;d.push_back(dist[u]); //将子树节点的距离加到vector中,最终求解时会用(这实际就是分析中的数组A) for(unsigned int i=0;i<G[u].size();++i){ if((v=G[u][i].v)==fa||done[v]) continue; dist[v]=dist[u]+G[u][i].w; getDist(v,u);siz[u]+=siz[v]; } } int calc(int key,int init){ //计算以key为根节点的路径数(包括重复的) d.clear();dist[key]=init;getDist(key,0); sort(d.begin(),d.end());int res=0; for(unsigned int i=0,l=d.size()-1;i<l;){ if(d[i]+d[l]<=K) res+=(int)(l-i++); else --l; } return res; } void work(int key){ ans+=calc(key,0);int v;done[key]=true; //每次考虑完某个节点以后要标记done数组,也表示对原树的分割 for(unsigned int i=0;i<G[key].size();++i){ if(done[v=G[key][i].v]) continue; ans-=calc(v,G[key][i].w); //这里的第二个参数,表示dist的参考点仍是根节点 dp[0]=Size=siz[v]; //设置初始值 getRoot(v,root=0); //找重心 work(root); //递归求解 } } int main(){ int u,v,w; while(scanf("%d%d",&n,&K)&&!(K==0&&n==0)){ memset(done,0,sizeof(done)); for(int i=1;i<n;++i){ scanf("%d%d%d",&u,&v,&w); G[v].push_back(Node(u,w)); G[u].push_back(Node(v,w)); } dp[0]=Size=n;getRoot(1,root=0); ans=0;work(root); printf("%d\n",ans); for(int i=1;i<=n;++i) G[i].clear(); } return 0; }反思:
树分治+求重心,我也算是八分之一的男人啦哈哈哈哈哈O(∩_∩)O~~
相关文章推荐
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1001
- POJ ACM 1002
- 1611:The Suspects
- POJ1089 区间合并
- POJ 2635 The Embarrassed Cryptographe
- POJ 3292 Semi-prime H-numbers
- POJ 2773 HAPPY 2006
- POJ 3090 Visible Lattice Points
- POJ-2409-Let it Bead&&NYOJ-280-LK的项链
- POJ-1695-Magazine Delivery-dp
- POJ1523 SPF dfs
- POJ-1001 求高精度幂-大数乘法系列
- POJ-1003 Hangover
- POJ-1004 Financial Management
- 用单调栈解决最大连续矩形面积问题
- 2632 Crashing Robots的解决方法
- 1573 Robot Motion (简单题)
- POJ 1200 Crazy Search(简单哈希)
- 【高手回避】poj3268,一道很水的dijkstra算法题