主要是利用了反证法:

假设 s-t这条路径为树的直径,或者称为树上的最长路

现有结论,从任意一点u出发搜到的最远的点一定是s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路

证明:

1    设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则

dis(u,T) >dis(u,s)     且  dis(u,T)>dis(u,t)   则最长路不是s-t了,与假设矛盾

2   设u不为s-t路径上的点

首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了

所以现在又有两种情况了:

1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)

2:u走到最远点的路径u-T与s-t无交点,则dis(u-T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,

则dis(u,T)+dis(s,X)+dis(u,X)>dis(s,X)+dis(X,t)=dis(s,t)最长路不是s-t矛盾

附上一张第二种情况的图

分类:  图论

树的“重心”的一些性质及动态维护

2011-08-24 20:31:13|  分类: 程序|字号 订阅

还记得曾经提到过的树的“重心”吗?重心的定义是:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

树的重心的一个的性质:

树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
这也是“道路修建”带来的启发。(证明:调整法)
树的重心的另一个性质:
把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
这个让“重心”名副其实了。。。(证明:。。。自己好好思考一下吧。。。)
还有一个性质:
把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
(证明:提示:张放想都没想说,要不然那两个不等式就矛盾了)
嗯,不错,有这么多性质,可以出不少恶心题了。。。
不过,我还是更关心一个事情:重心的动态维护。
如何动态呢?
情景1:添加一片叶子
根据已有性质,添加一片叶子之后新的重心要么不动要么向那片叶子的方向移动一下。这样,可以用一个link-cut tree来维护。
我们以重心为根建立这个动态树。每个节点维护它所在的子树的大小。添加叶子等于向一条路径上的维护值增加1,这个可以通过打标记实现。发现不得不移动的时候进行一次换根的操作。因为只可能移动1,所以换根的操作是可以完成的。
我们甚至还可以维护所有点到重心的距离和!这个只需给每个点加一个维护值:这个点为根的子树中所有点到这个点的距离和,通过稍微有点复杂的标记系统还是可以维护的。
情景2:删除一片叶子
只有删除操作?那么离线改成添加吧。。。
不允许离线?那么我们要换一个思路:
定义稍微广义的树的重心:每个点有一个非负权,每个边有个正的长度,到所有点的权乘以距离的和最小的点定义为重心。
注意:树的重心的位置和边的长度没有关系!。
在只有权值修改的情况下,我们可以利用树分治配合基本数据结构来维护树的重心:
注意到,我们可以维护一个子树内的点的权值和(利用dfs序)。这样给定一条边,我们就能够知道树的重心在这条边的哪边(看看哪边权值和大就行了)。
这样,我们可以先找一个比较靠近中心的边,问问应该向哪边走,再分治下去,就像树分治那样(类似二分查找?)。
当然,要想一下其他的技巧来对付”星型数据“,这个应该不难(通过拆点、拆边的技巧)。
利用这个广义一点的重心,我们发现,删除操作其实就是把权修改成0而已,可以在log^2N的时间内动态维护了。
如何处理多重心的情况?”抖动“一下权值使得只有一个重心不就行了。。。那另一个重心在哪里?这个只是个细节问题。。。
能否维护距离和?能否在logN的时间内维护?欢迎讨论(将子树和查询与树分治结合起来?。。。)。
情景3:移动一片叶子
把一个叶子移动到另一个地方。
这个怎么维护呢?其实,我们发现,新的重心应该在原来的重心和叶子新的位置的连线上(证明?应该是对的吧),移动距离很小。于是,也就可以维护了。
情景4:移动一个子树(被移动的子树小于原树的一半,并且保持它的根不变)
这个可以维护吗?
新的重心在原来重心和新子树的接合点的连线上吗?(证出来的欢迎留言)
有一个可以和link-cut tree配合使用的工具,叫做Euler-tour tree,它维护树的欧拉回路,基本元素是边。利用它,可以方便的完成子树的移动,并且给定一条有向边,可以回答这条边指向的子树的大小(Eurler-tour tree中没有”根“以及”父亲“这个概念!)。如果上面的那个论断是对的,那么这个应该可以维护了(复杂度?log^2N吧。。。)
另一个思路是树块划分,把树划分成若干联通块,并且在查询的时候合并相邻的联通块使得每个联通块的大小都是sqrt(N)的级别。这个东西对维护是否有帮助?欢迎交流。。。
情景5:开始N个一个点的树,每次用一条边合并两个树,要求回答新的树的重心
离线?在线?logN?log^2N?sqrt(N)?
等待你去探索

POJ 1655 - DP 树的重心,经典 #P

分类: #P DP 2013-06-16 09:40  119人阅读  评论(0)  收藏  举报
POJ 1655 - DP 树的重心,经典 #P
题意:求树的重心。
树的重心:删去重心后,生成的多棵树尽可能平衡。
重心的意义,在对树进行分治的时候可以避免N^2的极端复杂度(从退化链的一端出发),保证NlogN的复杂度。
解法:
一开始想到的是模仿求树的直径那样子去Dp,两次DFS。
son[i] - 结点i的儿子结点数目
第一遍求出son;
h[i] - 结点i向上的结点数目
h[i] = h[k] + son[k] - son[i] - 1;
blance = max(son[j] , h[i])
第二遍求出h,和blance;
后来去看题解,才发现有更简单的方法。
应用一个性质,h[i] = n - son[i] -1;
blance = max(son[j] , n - son[i] -1);
这样只需要一次DFS。
[cpp]  view plain copy
  1. #include <cstdio>
  2. #include <iostream>
  3. #include <fstream>
  4. #include <cstring>
  5. #include <string>
  6. #include <vector>
  7. #define OP(s) cout<<#s<<"="<<s<<" ";
  8. #define PP(s) cout<<#s<<"="<<s<<endl;
  9. using namespace std;
  10. int n;
  11. vector <int> adj[20010];
  12. int son[20010];
  13. bool vd[20010];
  14. int ans,asize = 1<<29;
  15. void DFS(int s)
  16. {
  17. vd[s] = 1;
  18. son[s] = 0;
  19. int blance = 0;
  20. int size = adj[s].size();
  21. for (int j = 0;j < size;j++)
  22. {
  23. int u = adj[s][j];
  24. if (vd[u]) continue;
  25. DFS(u);
  26. son[s] += son[u]+1;
  27. blance = max(blance,son[u]+1);
  28. }
  29. blance = max(blance,n - son[s] - 1);
  30. if (blance < asize || blance == asize && s < ans)
  31. ans = s,asize = blance;
  32. }
  33. int main()
  34. {
  35. //    freopen("test.txt","r",stdin);
  36. int T;
  37. cin>>T;
  38. while(T--)
  39. {
  40. cin>>n;
  41. for (int i = 1;i <= n;i++) adj[i].clear();
  42. for (int i = 1;i <= n-1;i++)
  43. {
  44. int u,v;
  45. scanf("%d%d",&u,&v);
  46. adj[u].push_back(v);
  47. adj[v].push_back(u);
  48. }
  49. memset(vd,0,sizeof(vd));
  50. asize = 1<<29;
  51. DFS(1);
  52. cout<<ans<<" "<<asize<<endl;
  53. }
  54. return 0;
  55. }

poj 1741 (树的分治)

分类: 数据结构 2013-10-07 11:04  80人阅读  评论(0)  收藏  举报
ACM 算法 编程 百度 分治

题意:给定一棵N(1<= N <=10000)个结点的带权树,定义dist(u,v)为u,v两点间的最短路径长度,路径的长度定义为路径上所有边的权和。再给定一个 K  ,如果对于不同的两个结点a,b,如果满足dist(a,b) <=K,则称(a,b)为合法点对。求合法点对个数。

思路:看了论文《分治算法在树的路径问题中的应用》,里面讲解的很清楚,一条路径要么过根节点,要么在一颗子树中,所以用分治算法。找到树的重心作为根节点,这样每次树的节点数至少减少一半。处理经过当前根节点路径<=k的点对数,然后把根节点去掉后就把原来的树分成几颗子树了,再处理子树。我们在求经过一个根节点的路径时,里面还包含了点对属于同一颗子树的情况,所以要去掉这部分的点。

dis(i)+dis(j)<=k(i,j的父节点不为根节点的同一个儿子)

=dis(i)+dis(j)<=k-dis(i)+dis(j)<=k(i,j的父节点属于根节点的同一儿子).

[cpp]  view plain copy
  1. #include <algorithm>
  2. #include<stdio.h>
  3. #include<string.h>
  4. const int N=10010;
  5. using namespace std;
  6. int head[N],num,f[N],son[N],n,D,root,size,ans,dis[N],d[N],cum;
  7. bool vis[N];
  8. #define max(a,b) (a<b?b:a)
  9. struct edge
  10. {
  11. int st,ed,w,next;
  12. }e[N*2];
  13. void addedge(int x,int y,int w)
  14. {
  15. e[num].st=x;e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;
  16. e[num].st=y;e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;
  17. }
  18. void getroot(int u,int father)//求树的重心
  19. {
  20. int i,v;
  21. f[u]=0;son[u]=1;
  22. for(i=head[u];i!=-1;i=e[i].next)
  23. {
  24. v=e[i].ed;
  25. if(vis[v]||v==father)continue;
  26. getroot(v,u);
  27. son[u]+=son[v];
  28. f[u]=max(f[u],son[v]);
  29. }
  30. f[u]=max(f[u],size-son[u]);
  31. if(f[u]<f[root])root=u;
  32. }
  33. void getdis(int u,int father)//求节点到根节点的距离
  34. {
  35. int i,v;
  36. son[u]=1;//更新子树的节点的子节点数,不更新也能ac
  37. d[cum++]=dis[u];//将点到根节点的距离加入数组
  38. for(i=head[u];i!=-1;i=e[i].next)
  39. {
  40. v=e[i].ed;
  41. if(vis[v]||v==father)continue;
  42. dis[v]=dis[u]+e[i].w;
  43. getdis(v,u);
  44. son[u]+=son[v];
  45. }
  46. }
  47. int cont(int u,int mit)
  48. {
  49. int res=0,L,R;
  50. dis[u]=mit;
  51. cum=0;
  52. getdis(u,0);
  53. sort(d,d+cum);//将点到根节点的距离排序
  54. for(L=0,R=cum-1;L<R;)
  55. {
  56. if(d[L]+d[R]<=D)//如果d[L]+d[R]<=D,L代表的节点可以与(R-L)个节点成对
  57. res+=(R-L++);
  58. else R--;
  59. }
  60. return res;
  61. }
  62. void work(int u)
  63. {
  64. int i,v;
  65. vis[u]=true;
  66. ans+=cont(u,0);//路径经过该根节点的点对数
  67. for(i=head[u];i!=-1;i=e[i].next)
  68. {
  69. v=e[i].ed;
  70. if(vis[v])continue;
  71. ans-=cont(v,e[i].w);//减去属于v子树的点对数
  72. root=0;f[root]=size=son[v];
  73. getroot(v,0);//求v子树的根节点
  74. work(root);//求v子树的点对
  75. }
  76. }
  77. int main()
  78. {
  79. int i,x,y,w;
  80. while(scanf("%d%d",&n,&D),n||D)
  81. {
  82. memset(head,-1,sizeof(head));
  83. num=0;
  84. for(i=1;i<n;i++)
  85. {
  86. scanf("%d%d%d",&x,&y,&w);
  87. addedge(x,y,w);
  88. }
  89. memset(vis,false,sizeof(vis));
  90. root=0;f[root]=size=n;ans=0;
  91. getroot(1,0);
  92. work(root);
  93. printf("%d\n",ans);
  94. }
  95. return 0;
  96. }

WC2010 重建计划

This post is written in Chinese. If you have trouble to read it, please use Google Translate

问题简述

给定一棵边上带权的树,求一个平均价值最大的简单路径。路径的平均价值定义为路径的带权长度与不带权长度的比值。

问题分析

在一棵树上直接找这样的路径是很困难的,因此我们考虑将问题分解。一个基本的想法是在树上分治,为保证对数级别的时间复杂度,必须使用基于点的剖分[1]。每次找到树的重心[2]作为根节点,然后将根节点的子树平均分为两部分,两部分共用根节点。对每一部递归求解,然后把这两部分合并。求树的重心的方法是随便找一个根,求出每个子树的大小,找到max{i.child.size,N-i.size}最小的i,i就是树的重心。重心可能有多个,找一个即可。

对于一个分治的局面,每一部分都是当前根节点的一些子树组成的森林,再加上根节点,所以每一部分仍然是一棵树。最优的路径可能在某一个分治的部分中,也可能跨过根节点在两个部分中。前者可以直接递归下去求解,重点是处理后者的情况。这时需要做的一个重要转化是二分答案,由于答案的范围是已知的,我们可以在答案的范围内二分答案的值A,然后把树上每一条边的权值都减去A。判断有解的方法也就变成了判断是否存在一条带权长度大于等于0的路径,继续转化就是,判断最长的带权路径的带权长度是否大于等于0

如何找出跨两部分的最长带权路径呢?由于路径的长度必须满足在[L,U]之间,简单的想法是在一个部分中枚举路径的一个端点的深度i,那么这条路径的另一端在另一个部分中的深度一定是在[L-i,U-i]之间。为保证路径最长,第一个部分中深度为i的一段显然应该是这个部分中深度为i的所有路径中带权长度最大的那一条,第二部分也同理,不过要枚举深度在[L-i,U-i]的最大值。如果我们确定i的枚举顺序以后,[L-i,U-i]区间的移动就是单调的,因此可以用单调队列维护最大值,因此时间复杂度就是线性的。

算法描述

  1. 求出当前树的重心,对当前树进行点剖分。

  2. 二分当前树中平均长度最大值A,判断二分范围是否满足精度,如果满足转到步骤5,否则转到步骤3。

  3. 将树上所有边权值减去A,求出剖分的两部分每个深度上的最长带权路径长度。

  4. 用单调队列维护,求出跨两部分的带权路径长度最大值,判断该值是否大于等于0,转到步骤2。

  5. 对剖分的两部分分别递归求解,如果这一部分大小大于等于L的话。

复杂度分析

对树点剖分的时间复杂度为O(logN),求重心的时间复杂度为O(N),二分答案时间复杂度为O(logV),求带权路径长度最大值时间复杂度为O(N),因此总时间复杂度为O(NlogNlogV)。

参考程序

/* * Problem: NOI Winter Camp 2010 Rebuild* Author: Guo Jiabao* Time: 2010.3.12 14:01* Label: Solved* Memo: Binary Search + Monoqueue + Devide & Conquer on tree
*/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <sstream>
#include <vector>
#include <list>
#include <deque>
#include <string>
#include <queue>
using namespace std;
#define var(a,b) typeof(b) a(b)
#define foreach(a,b) for (var(a,b.begin());a!=b.end();++a)const int MAXN = 100001,MAXM=MAXN*2,INF=~0U>>1;
const double LIM=1e6,FINF = 1e20;struct Monoqueue
{struct element{int key;double value;}Q[MAXN];int head,rear;void reset(){rear = 0;head = 1;}void insert(int k,double v){while (rear >= head && v > Q[rear].value)rear--;Q[++rear].value = v;Q[rear].key = k;}int getmax(int L){while (Q[head].key < L)head++;return Q[head].key;}
}MQ;struct edge
{edge *next;int t,c;
};int N,L,U,EC,timestamp,Total;
int t_ctd,t_ctd_csm,md1,md2,*t_maxdpt;
int size[MAXN],depth[MAXN];
double length[MAXN],d1m[MAXN],d2m[MAXN],*t_dm;
edge *V[MAXN],ES[MAXM];
int ava[MAXN];
double Ans,t_delta;inline void addedge(int a,int b,int c)
{edge *e=ES+ ++EC;e->next = V[a];e->t = b;e->c = c;V[a] = e;
}void init()
{freopen("rebuild.in","r",stdin);freopen("rebuild.out","w",stdout);scanf("%d%d%d",&N,&L,&U);for (int i=1;i<N;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);addedge(a,b,c);addedge(b,a,c);if (L == 1 && c > Ans)Ans = c;}
}void get_depth(int i)
{for (edge *e=V[i];e;e=e->next){int j = e->t;if (ava[j] != 0) continue;if (depth[j] == -1){depth[j] = depth[i] + 1;get_depth(j);}}
}int get_longest_line(int start)
{int i,maxdepth;memset(depth,-1,sizeof(depth));depth[start] = maxdepth = 0;get_depth(start);for (i=1;i<=N;i++)if (ava[i] == 0 && depth[i] > maxdepth){maxdepth = depth[i];start = i;}memset(depth,-1,sizeof(depth));depth[start] = maxdepth = 0;get_depth(start);for (i=1;i<=N;i++)if (ava[i] == 0 && depth[i] > maxdepth)maxdepth = depth[i];return maxdepth;
}void get_size(int i)
{int csm = 0;size[i] = 1;for (edge *e=V[i];e;e=e->next){int j = e->t;if (ava[j] != 0) continue;if (size[j] == -1){get_size(j);size[i] += size[j];if (size[j] > csm)csm = size[j];}}if (Total - size[i] > csm)csm = Total - size[i];if (csm < t_ctd_csm){t_ctd_csm = csm;t_ctd = i;}
}int get_centroid(int i)
{memset(size,-1,sizeof(size));t_ctd_csm = INF;get_size(i);memset(size,-1,sizeof(size));return t_ctd;
}void count_size(int i)
{size[i] = 1;for (edge *e=V[i];e;e=e->next){int j = e->t;if (ava[j] != 0) continue;if (size[j] == -1){count_size(j);size[i] += size[j];}}
}void count_depth_max_length(int i)
{//求最大深度if (depth[i] > *t_maxdpt)*t_maxdpt = depth[i];for (edge *e=V[i];e;e=e->next){int j = e->t;if (ava[j] != 0) continue;//求該深度最大帶權長度if (length[i] > t_dm[depth[i]])t_dm[depth[i]] = length[i];if (depth[j] == -1){length[j] = length[i] + e->c - t_delta;depth[j] = depth[i] + 1;count_depth_max_length(j);}}
}bool check(double delta,int ctd,edge *f)
{edge *e;int i,j,k;for (i=0;i<=N;i++)d1m[i] = d2m[i] = -FINF;t_delta = delta;memset(depth,-1,sizeof(depth));memset(length,-1,sizeof(length));md1 = md2 = 0;length[ctd] = depth[ctd] = 0;//統計部份1t_dm = d1m;t_maxdpt = &md1;for (e=V[ctd];e != f;e=e->next){int j=e->t;if (ava[j] !=0) continue;length[j] = e->c - t_delta;depth[j] = 1;count_depth_max_length(j);}//統計部份2t_dm = d2m;t_maxdpt = &md2;for (e=f;e;e=e->next){int j=e->t;if (ava[j] !=0) continue;length[j] = e->c - t_delta;depth[j] = 1;count_depth_max_length(j);}//單調隊列維護最大值//確定左邊界i = U-1;if (i > md1)i = md1;//確定右邊界k = L-i;if (k < 1)k = 1;MQ.reset();for (j=k;j<U-i && j<=md2 ;j++)MQ.insert(j,d2m[j]);double curv,maxv=-INF;for (;i>0 && L-i<=md2;i--){j = U-i;if (j<=md2)MQ.insert(j,d2m[j]);int k = MQ.getmax(L-i);curv = d1m[i] + d2m[k];if (curv > maxv)maxv = curv;}return maxv >= 0;
}double binary(int ctd,edge *f)
{double a=0,b=LIM,m;while (b - a >= 0.0001){m = (a+b)/2;if (check(m,ctd,f))a = m;elseb = m;}return a;
}void dct(int start)
{int nowt = ++timestamp;int line = get_longest_line(start);if (line<=L || line<=2)return;int ctd = get_centroid(start); //取得重心double cur;//分割兩部份count_size(ctd);int pls = 0,pls2 = 0;edge *e,*f;for (e=V[ctd];e;e=e->next){int j=e->t;if (ava[j] !=0) continue;pls += size[j];if (pls + pls + 1 >= size[ctd] - 1){f = e->next;pls2 = size[ctd] - pls;pls++;break;}}//合併兩部份cur = binary(ctd,f);if (cur > Ans)Ans = cur;//遞歸部份1int j;if (pls-1 >= L){for (e=f;e;e=e->next)if (ava[j=e->t] ==0)ava[j] = nowt;Total = pls;dct(ctd);for (edge *e=f;e;e=e->next)if (ava[j=e->t] ==nowt)ava[j] = 0;}//遞歸部份2if (pls2-1 >= L){for (e=V[ctd];e!=f;e=e->next)if (ava[j=e->t] ==0)ava[j] = nowt;Total = pls2;dct(ctd);for (e=V[ctd];e!=f;e=e->next)if (ava[j=e->t] ==nowt)ava[j] = 0;}
}void solve()
{memset(ava,0,sizeof(ava));Total = N;dct(1);
}int main()
{init();solve();printf("%.3lf\n",Ans);return 0;
}

[1]  具体证明参见2009年集训队论文《分治算法在树的路径问题中的应用》。

[2] 树的重心就是满足“删除该点后,剩余的最大的子树顶点数最少”的点。

树的直径,树的重心,树的分冶相关推荐

  1. 模板 - 树上问题(树的直径、动态查询树的直径、树的重心)

    整理的算法模板合集: ACM模板 目录 一.树的直径 树形DP 两次DFS / BFS(找到直径的两个端点) 二.动态修改树的边权并求每个时刻的直径(线段树) 三.树的重心 一.树的直径 树的直径满足 ...

  2. 【leetcode-DFS】求根节点到叶节点数字之和/二叉树的最大路径和/路径总和/左叶子之和/树的直径

    文章目录 求根节点到叶节点数字之和 深度优先搜索 广度优先搜索 二叉树的最大路径和 DFS 路径总和1 深度优先搜索 广度优先搜索 路径总和2 回溯法 广度优先搜索 左叶子之和 递归 DFS 树的直径 ...

  3. 树形结构——树的直径

    树的直径的定义  树的直径:树上最远两点(叶子结点)的距离. 树的直径的求法 例题:[模板]树的直径 两遍暴力dfs 引理:对于树上任意一点 P P P,找到离它最远的节点 Q Q Q.在找到离节点 ...

  4. 树的直径1——树的直径

    树的直径1--树的直径 目录 题目:树的直径1 题面 题目描述 输入描述 输出描述 样例输入 样例输出 数据范围 代码及思路 解题思路 AC代码 知识点:树的直径 定义 性质 查找"树的直径 ...

  5. 树的直径,树的中心,树的重心

    树的直径,树的中心,树的重心 树的直径:树上两点间最长距离所对应的路径 求法一:树形dp: 从每个点向下搜索,找到以该点为根到子节点的最大值和次大值 然后将次大值和最大值相加,即求得经过该点的最长路径 ...

  6. 树的直径/重心 学习笔记

    树的直径 POJ2631 Roads in the North 题意:裸的直径 题解:套模板,但是开始的时候,我的代码在第一次dfs循环的时候,没有考虑到,路长全为0的情况,而用来记录最远点的maxp ...

  7. [51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询

    题意: 给你一棵带权树,q次查询,每次给出两个区间,[l1,r1][l2,r2][l_1,r_1] [l_2,r_2][l1​,r1​][l2​,r2​]从这两个区间中分别选择两个数字,使得这两个点的 ...

  8. 小A与欧拉路(牛客-树的直径)

    题解: 欧拉路:从图中任意一个点开始到图中任意一个点结束的路径,并且图中每条边只通过恰好一次 问你走完这树上所有的点最短路径是什么. 因为树是没有环的,所以你走到叶子结点的时候需要往回走,也就是再走一 ...

  9. luogu P4408 [NOI2003]逃学的小孩(树的直径)

    整理的算法模板合集: ACM模板 看了半天的题原来没有告诉你三个点的坐标,不然直接跑最短路即可.要求最长的时间,所以我们要自己找到三个点,而在树中最长的路径也就是树的直径.那么本题就可以简化为:在一棵 ...

最新文章

  1. Python学习之关键要素
  2. Java学习---流与文件
  3. java中统计括号配对_括号配对问题(C语言或JAVA语言代码)
  4. multisim中轻触开关在哪_现货供应轻触开关|品质确保|厂家直销
  5. Tensorflow 之 name/variable_scope 变量管理
  6. HOW2J 全套教程整理:Java、前端、数据库、中间件、第三方、项目、面试题
  7. php得到第一个数组_php怎么获取数组第一个元素
  8. ASP.NET AJAX学习记要(1)-如何下手
  9. opencv 显示图片失败,全灰
  10. 单片机 自动更改日期_自动螺丝机不可或缺之功能
  11. iPhone iPad游戏应用开发视频教程
  12. 非线性系统 知识梳理
  13. 端口错误 给Arduino下载程序提示
  14. 怎么在html5中视频加字幕,视频转换王如何加字幕 在视频中添加文字的方法
  15. 大数据常用命令-超全
  16. 智能云服务器选型,智能主机概述、选型及应用
  17. 这个社会最大的现实是“大鱼吃小鱼,小鱼吃虾米”
  18. 2019ROS暑期学校之如故如新
  19. 白醋泡大蒜,治疗灰指甲。以下为详细操作,供需要的人参考
  20. js单行代码------对象

热门文章

  1. MacOS QQ红包助手 (消息防撤回、自动抢红包)
  2. matplotlib plot python rgb2gry 显示灰度图像
  3. 作为投资者,这10个对冲策略你必须烂熟于心
  4. cellpadding和 cellspacing
  5. 科技云报道:车云协同,云计算下一个主战场?
  6. stm32语音识别文字显示_STM32实现孤立词语音识别系统
  7. 华为鸿蒙周易,世纪工程背后的大局--港珠澳大桥的风水秘密!
  8. 黄金期货单位(黄金期货单位有哪些)
  9. 用户与计算机的交互界面是什么,一文解读:什么是用户界面设计?
  10. n1进入recovery模式_如何进入OPPO N1的Recovery模式(安卓系统适用)