50 条题解
- 
  6huxiuhan LV 3 @ 2008-11-18 19:43:04 算法1. 
 首先先说只用1次BFS求出无权无根树最长路的方法
 类似拓扑排序,每次将所有度为1的点一起删除,直到最后剩下点数不超过2,那么删除的次数*2+剩下的顶点数即为这棵无根树的最长路径长度。
 这么做的原理是找最长路的两端(必然在叶子节点),那么不断删除叶子节点,生成新的无根树,同样进行。
 那么同样地,对于这题只要稍作改变即可。
 当每轮删除度为1的点时(记得每轮是把所有度为1的点一起删除),如果它相邻的顶点度不为1,即它在新的树不是叶子节点,那么这个所删除的点必然不在最长路径上,将这些点权值赋为false,其它点为true,最后从最后剩下的点开始,对于权为true的顶点进行一次floodfill一次即可
 Use this way
 ├ 测试数据 01:答案正确... 0ms
 ├ 测试数据 02:答案正确... 0ms
 ├ 测试数据 03:答案正确... 0ms
 ├ 测试数据 04:答案正确... 0ms
 ├ 测试数据 05:答案正确... 0ms
 ├ 测试数据 06:答案正确... 0ms
 ├ 测试数据 07:答案正确... 0ms
 ├ 测试数据 08:答案正确... 41ms
 ├ 测试数据 09:答案正确... 72ms
 ├ 测试数据 10:答案正确... 41ms
- 
  1@ 2015-11-03 13:09:09这个题困扰了我好久好久 
 动规的那个方法不算太难
 但是我更喜欢用dfs过他树上的最长链的长度只需做两边dfs 这个大概都知道 
 现在的问题是怎么求在链上的点
 因为可能有很多条 所以我们还要再dfs一次第一次dfs 随便一个点当成根 求最远点 
 第二次dfs 用第一次求出的最远点当根 求最远点 这时就求出来最长链的长度
 第三次dfs 用第二次求出的最远点中的任意一个当根 如果某点的深度等于最长链的长度 此点也在最长链上这样就避免了最长链的两端的分支被忽视的情况 代码 
 type
 arr=record
 v,next:longint;
 end;
 var
 t:array[0..400009] of arr;
 deep,last:array[0..200009] of longint;
 b:array[0..200009] of boolean; //是不是在最长链上
 md,gen,tot,i,x,y,n:longint;
 procedure built(x,y:longint);
 begin
 inc(tot);
 t[tot].v:=y;
 t[tot].next:=last[x];
 last[x]:=tot;
 end;
 procedure dfs(x,d:longint); //x点的深度为d
 var j,p:longint;
 begin
 deep[x]:=d;
 j:=last[x];
 while j<>0 do
 begin
 p:=t[j].v;
 if deep[p]=0 then dfs(p,d+1);
 j:=t[j].next;
 end;
 end;
 procedure zou(x:longint); //寻找哪个点在最长链上 一直向上走 重复的不再走
 var p,j:longint;
 begin
 b[x]:=true;
 j:=last[x];
 while j<>0 do
 begin
 p:=t[j].v;
 if (b[p]=false) and (deep[p]<deep[x]) then zou(p);
 j:=t[j].next;
 end;
 end;
 begin
 read(n);
 for i:=1 to n-1 do
 begin
 read(x,y);
 inc(x); inc(y); //编号都加1 方便处理
 built(x,y);
 built(y,x);
 end;
 dfs(1,1); //第一次dfs 我是用的1为根
 for i:=1 to n do
 if deep[i]>md then
 begin md:=deep[i]; gen:=i; end; //找到最远点
 fillchar(deep,sizeof(deep),0);
 dfs(gen,1); //第二次dfs
 md:=0;
 for i:=1 to n do
 if deep[i]>md then md:=deep[i]; //求出最长链的长度
 fillchar(b,sizeof(b),false);
 for i:=1 to n do //然后开始走
 if deep[i]=md then zou(i);
 fillchar(deep,sizeof(deep),0);
 for i:=1 to n do
 if deep[i]=md then gen:=i; //再寻找一个最远点当成根
 dfs(gen,1); //第三次dfs
 for i:=1 to n do //再走一次
 if deep[i]=md then zou(i);
 for i:=1 to n do //输出时序号减1
 if b[i]=true then writeln(i-1);
 end.
- 
  1@ 2009-11-06 20:37:42第300题,O(∩_∩)O哈哈~ Accepted 有效得分:100 有效耗时:632ms 我的猥琐DP…… 感觉不够快 d1[]表示以这个点为根往下的最大深度 
 d2[]次大
 up[]通过它父亲绕过来的最大长度d1,d2[] DFS一下就出来了 
 up[] 用DP来算想DP的同学随便找个点(一般就是1号点啦),画成树状的,对于某个点,如果要它构成最长路径只有两种可能。 一:由它儿子的最大深度和次大深度构成。 
 二:有一条路径是从它爹上面下来
 不过这有两种
 (1)这个点x在它爹的最大深度路径上,up[x]=max(up[父亲]+1,d2[父亲]+1)
 (2)不在它爹的最大深度路径上,up[x]=max(up[父亲]+1,d1[父亲]+1)这两种情况,画一下图就知道了。 那么通过该点的最长路径就是max(up[x]+d1[x],d2[x]+d1[x]); 
- 
  1@ 2008-11-09 17:17:53方法1: 
 首先先说只用1次BFS求出无权无根树最长路的方法。
 类似拓扑排序,每次将所有度为1的点一起删除,直到最后剩下点数不超过2,那么删除的次数*2+剩下的顶点数即为这棵无根树的最长路径长度。
 这么做的原理是找最长路的两端(必然在叶子节点),那么不断删除叶子节点,生成新的无根树,同样进行(也可以理解为从直径两端不断向中间伸展)。
 那么,如何求在最长路上的点呢?
 当每轮删除度为1的点时(记得每轮是先把所有度为1的点一起删除),如果它相邻的顶点度不为1,即它在新的树不是叶子节点,那么这个所删除的点必然不在最长路径上,将这些点权值赋为false,其它点为true,最后从最后剩下的点开始,对于权为true的顶点进行一次floodfill一次即可。 
 算法正确性证明:
 若一个顶点在直径内,则经过这个顶点的直径只可能是以下两种情况:
 以这个顶点为根建树
 1.存在多个以某儿子为根的高度最大的子树,则这些儿子都经过直径,其他儿子就会被标成false。
 2.存在一个以某儿子为根的高度最大的子树,和多个以其他儿子为根的高度次大的子树,则这些儿子也都经过直径,其他点也会被标成false。因此可以递归调用处理,也就是floodfill。 
 by TN方法2: 
 从任意一点P出发找一个最远点Q(定理1),然后以Q为根建树,找以某儿子为根的子树中高度最大和次大的儿子,则这些儿子都为直径中的点,递归调用即可。定理1: 
 最远点Q一定在直径上。证明: 
 1.若P在直径上,则Q一定在直径上
 2.若P不在直径上,则设直径为AB,若AB与PQ有交点,则B一定=Q;若AB与PQ无交点,PQ上某一点M到AB上某一点N(MN不经过AB和PQ中的任意一点),则MQ+MN
- 
  0@ 2017-11-03 12:29:55#include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #include <vector> #include <deque> #include <limits> #include <string> #include <sstream> using namespace std; const int oo_min=0xcfcfcfcf,oo_max=0x3f3f3f3f; int n,l; int R=0; int fa[200000]; int vis[200000]; int up[200000]; int down_1[200000]; int down_2[200000]; int down_1_p[200000]; int down_2_p[200000]; vector<vector<int> > s; void work_1(int now) { vis[now]=1; for (int i=0;i<s[now].size();i++) if (vis[s[now][i]]==0) { fa[s[now][i]]=now; work_1(s[now][i]); if (down_1[now]<down_1[s[now][i]]+1) { down_2[now]=down_1[now]; down_2_p[now]=down_1_p[now]; down_1[now]=down_1[s[now][i]]+1; down_1_p[now]=s[now][i]; } else if (down_2[now]<down_1[s[now][i]]+1&&down_1[s[now][i]]+1<=down_1[now]) { down_2[now]=down_1[s[now][i]]+1; down_2_p[now]=s[now][i]; } } } void work_2(int now) { if (now!=R) { if (down_1_p[fa[now]]!=now) up[now]=max(up[fa[now]]+1,down_1[fa[now]]+2); else up[now]=max(up[fa[now]]+1,down_2[fa[now]]+2); l=max(l,up[now]); } else up[now]=1; for (int i=0;i<s[now].size();i++) if (s[now][i]!=fa[now]) work_2(s[now][i]); } int main() { while (~scanf("%d",&n)) { s.clear(); s.resize(n); for (int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); s[x].push_back(y); s[y].push_back(x); } memset(fa,0,sizeof(fa)); memset(up,0,sizeof(up)); memset(down_1,0,sizeof(down_1)); memset(down_2,0,sizeof(down_2)); memset(down_1_p,-1,sizeof(down_1_p)); memset(down_2_p,-1,sizeof(down_2_p)); memset(vis,0,sizeof(vis)); work_1(R); l=0; work_2(R); for (int i=0;i<n;i++) if (up[i]+down_1[i]==l||down_1[i]+down_2[i]+1==l) printf("%d\n",i); } }
- 
  0@ 2016-01-24 22:35:32简直了,一开始只加了单向边,错了好几次才发现..... 
 #include <cstdio>
 #include <cstdlib>
 #include <cctype>
 #include <algorithm>
 #define rep(i, x, y) for (int i = x; i <= y; ++i)using namespace std; const int maxn = 200005; 
 int pool_cnt;struct Node { 
 int nextNode;
 Node *next;
 } *N[maxn], pool[maxn << 1];int n; inline void AddEdge(int x, int y) { 
 Node *p = &pool[++pool_cnt];
 *p = (Node){y, N[x]};
 N[x] = p;
 }
 inline int read() {
 int r = 0;
 char p = getchar();
 while (!isdigit(p)) p = getchar();
 while (isdigit(p)) {r *= 10; r += (p - '0'); p = getchar();}
 return r;
 }
 bool vis1[maxn];
 int down1[maxn], down2[maxn], father[maxn];
 void dfs1(int k) {
 vis1[k] = true;
 for (Node *p = N[k]; p; p = p->next) {
 if (!vis1[p->nextNode]) {
 father[p->nextNode] = k;
 dfs1(p->nextNode);
 if (down1[p->nextNode] + 1 > down1[k]) {
 down2[k] = down1[k];
 down1[k] = down1[p->nextNode] + 1;
 }
 else if (down1[p->nextNode] + 1 > down2[k]) down2[k] = down1[p->nextNode] + 1;
 }
 }
 }
 bool vis2[maxn];
 int up[maxn];
 void dfs2(int k) {
 if (k != 0) {
 up[k] = up[father[k]] + 1;
 if (down1[father[k]] - 1 == down1[k]) up[k] = max(up[k], down2[father[k]] + 1); else up[k] = max(up[k], down1[father[k]] + 1);
 }
 vis2[k] = true;
 for (Node *p = N[k]; p; p = p->next) {
 if (!vis2[p->nextNode]) {
 dfs2(p->nextNode);
 }
 }
 }
 int length[maxn];
 int main(int argc, const char *argv[]) {
 scanf("%d", &n);
 int x, y;
 rep(i, 1, n - 1) {
 scanf("%d %d", &x, &y);
 AddEdge(x, y);
 AddEdge(y, x);
 }
 dfs1(0);
 dfs2(0);
 int maxl = 0;
 rep(i, 0, n - 1) {
 // printf("number:%d first:%d second:%d up:%d\n", i, down1[i], down2[i], up[i]);
 length[i] = down1[i] + max(up[i], down2[i]);
 maxl = max(maxl, length[i]);
 }
 rep(i, 0, n - 1) {
 if (length[i] == maxl) printf("%d\n", i);
 }
 return 0;
 }
- 
  0@ 2015-11-03 13:11:03忘了发这个 速度应该算是快的了吧 楼下是我的解析 测试数据 #0: Accepted, time = 0 ms, mem = 5332 KiB, score = 10 
 测试数据 #1: Accepted, time = 0 ms, mem = 5328 KiB, score = 10
 测试数据 #2: Accepted, time = 0 ms, mem = 5328 KiB, score = 10
 测试数据 #3: Accepted, time = 0 ms, mem = 5328 KiB, score = 10
 测试数据 #4: Accepted, time = 0 ms, mem = 5328 KiB, score = 10
 测试数据 #5: Accepted, time = 0 ms, mem = 5328 KiB, score = 10
 测试数据 #6: Accepted, time = 46 ms, mem = 5328 KiB, score = 10
 测试数据 #7: Accepted, time = 109 ms, mem = 5328 KiB, score = 10
 测试数据 #8: Accepted, time = 93 ms, mem = 5332 KiB, score = 10
 测试数据 #9: Accepted, time = 93 ms, mem = 5336 KiB, score = 10
 Accepted, time = 341 ms, mem = 5336 KiB, score = 100
- 
  0@ 2013-11-03 21:14:26AC纪念... 
 编译成功测试数据 #0: Accepted, time = 0 ms, mem = 11196 KiB, score = 10 
 测试数据 #1: Accepted, time = 15 ms, mem = 11192 KiB, score = 10
 测试数据 #2: Accepted, time = 7 ms, mem = 11196 KiB, score = 10
 测试数据 #3: Accepted, time = 0 ms, mem = 11196 KiB, score = 10
 测试数据 #4: Accepted, time = 0 ms, mem = 11196 KiB, score = 10
 测试数据 #5: Accepted, time = 15 ms, mem = 11192 KiB, score = 10
 测试数据 #6: Accepted, time = 109 ms, mem = 11196 KiB, score = 10
 测试数据 #7: Accepted, time = 265 ms, mem = 11192 KiB, score = 10
 测试数据 #8: Accepted, time = 281 ms, mem = 11196 KiB, score = 10
 测试数据 #9: Accepted, time = 265 ms, mem = 11196 KiB, score = 10
 Accepted, time = 957 ms, mem = 11196 KiB, score = 100
 咱的方法是两次dfs,求每个节点子树下的次长路,最长路,和以它为起点,向父亲走的最长路
 然后每个节点的最长路就等于 次长路+最长路 或者 最长路+向父亲走的最长路 的最大值。
 然后扫一遍如果该节点的最长路等于最长路输出就可以了
- 
  0@ 2012-08-28 21:33:05我与其他的人做法大概相同,稍微改进的就是: 1、先找出这棵树的直径上的中间点,若直径的点的个数是偶数个,那么在最中间增加一个虚拟节点,设他为这个树的核心 2、然后由这个核心作为七天,DFS,如果DFS到某一个点的长度为半径,那么这条路径上的点都是符合条件的,标记一下就行了。 3、这样的好处就是不用考虑很多特殊情况,开始纠结从哪些点开始搜索浪费了很多时间,事实上从核心开始搜索是最快的!! 
- 
  0@ 2009-11-07 14:22:14Accepted 有效得分:100 有效耗时:175ms 悲剧了我的图论啊……看了题解的思路,终于弄懂floodfill的方法,调试了好久才出来(第一次用到了指针T^T)…… 
 这个……难度1……
- 
  0@ 2009-10-27 10:11:09三遍bfs,细节很多 
- 
  0@ 2009-10-22 14:23:48编译通过... 
 ├ 测试数据 01:答案正确... 0ms
 ├ 测试数据 02:答案正确... 0ms
 ├ 测试数据 03:答案正确... 0ms
 ├ 测试数据 04:答案正确... 0ms
 ├ 测试数据 05:答案正确... 0ms
 ├ 测试数据 06:答案正确... 41ms
 ├ 测试数据 07:答案正确... 119ms
 ├ 测试数据 08:答案正确... 353ms
 ├ 测试数据 09:答案正确... 650ms
 ├ 测试数据 10:答案正确... 338ms
 ---|---|---|---|---|---|---|---|-
 Accepted 有效得分:100 有效耗时:1501ms
 有点慢~ ~
- 
  0@ 2008-11-13 20:49:00一开始用数组总是溢出,后来经大牛提醒,原来要用指针。 
 唉,花了8个小时,终于AC了。
- 
  0@ 2008-11-11 22:23:26编译通过... 
 ├ 测试数据 01:答案正确... 0ms
 ├ 测试数据 02:答案正确... 0ms
 ├ 测试数据 03:答案正确... 0ms
 ├ 测试数据 04:答案正确... 0ms
 ├ 测试数据 05:答案正确... 0ms
 ├ 测试数据 06:答案正确... 0ms
 ├ 测试数据 07:答案正确... 0ms
 ├ 测试数据 08:答案正确... 166ms
 ├ 测试数据 09:答案正确... 56ms
 ├ 测试数据 10:答案正确... 72ms
 ---|---|---|---|---|---|---|---|-
 Accepted 有效得分:100 有效耗时:294ms比赛时貌似也是这么写的诶 
 怎么重写就ac了
- 
  0@ 2008-11-11 16:02:17编译通过... 
 ├ 测试数据 01:答案正确... 0ms
 ├ 测试数据 02:答案正确... 0ms
 ├ 测试数据 03:答案正确... 0ms
 ├ 测试数据 04:答案正确... 0ms
 ├ 测试数据 05:答案正确... 0ms
 ├ 测试数据 06:答案正确... 0ms
 ├ 测试数据 07:答案正确... 0ms
 ├ 测试数据 08:答案正确... 244ms
 ├ 测试数据 09:答案正确... 291ms
 ├ 测试数据 10:答案正确... 197ms
 ---|---|---|---|---|---|---|---|-
 Accepted 有效得分:100 有效耗时:732ms
 比赛时40分。。。想了很久还是不知道哪错了,结果把DFS在原模原样作一遍就AC!
- 
  0@ 2008-11-11 08:51:08考试的时候竟然能想错...... 
 5555555...
 今天一次AC...其实不就是树网的核吗...
- 
  0@ 2008-11-10 21:49:28编译通过... 
 ├ 测试数据 01:答案正确... 0ms
 ├ 测试数据 02:答案正确... 0ms
 ├ 测试数据 03:答案正确... 0ms
 ├ 测试数据 04:答案正确... 0ms
 ├ 测试数据 05:答案正确... 0ms
 ├ 测试数据 06:答案正确... 0ms
 ├ 测试数据 07:答案正确... 0ms
 ├ 测试数据 08:答案正确... 150ms
 ├ 测试数据 09:答案正确... 150ms
 ├ 测试数据 10:答案正确... 166ms
 ---|---|---|---|---|---|---|---|-
 Accepted 有效得分:100 有效耗时:466ms大家好像都比我快 
 考试时忘加递归了,20
 现在一次AC
- 
  0@ 2008-11-10 21:20:37两次dp。第一次求每个点到它的叶子的最长次长路径长度,第二次把根以上的长度拿来修正这个最长次长路径,最后求出最长+次长=l的就是 
- 
  0@ 2008-11-10 20:52:50编译通过... 
 ├ 测试数据 01:答案正确... 0ms
 ├ 测试数据 02:答案正确... 0ms
 ├ 测试数据 03:答案正确... 0ms
 ├ 测试数据 04:答案正确... 0ms
 ├ 测试数据 05:答案正确... 0ms
 ├ 测试数据 06:答案正确... 0ms
 ├ 测试数据 07:答案正确... 0ms
 ├ 测试数据 08:答案正确... 134ms
 ├ 测试数据 09:答案正确... 103ms
 ├ 测试数据 10:答案正确... 119ms
 ---|---|---|---|---|---|---|---|-
 Accepted 有效得分:100 有效耗时:356ms
- 
  0@ 2008-11-10 20:37:03编译通过... 
 ├ 测试数据 01:答案正确... 0ms
 ├ 测试数据 02:答案正确... 0ms
 ├ 测试数据 03:答案正确... 0ms
 ├ 测试数据 04:答案正确... 0ms
 ├ 测试数据 05:答案正确... 0ms
 ├ 测试数据 06:答案正确... 0ms
 ├ 测试数据 07:答案正确... 0ms
 ├ 测试数据 08:答案正确... 41ms
 ├ 测试数据 09:答案正确... 56ms
 ├ 测试数据 10:答案正确... 41ms