HDU 4612 Warm up(双连通分量缩点+求树的直径)
Posted icode-xiaohu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 4612 Warm up(双连通分量缩点+求树的直径)相关的知识,希望对你有一定的参考价值。
思路:强连通分量缩点,建立一颗新的树,然后求树的最长直径,然后加上一条边能够去掉的桥数,就是直径的长度。
树的直径长度的求法:两次bfs可以求,第一次随便找一个点u,然后进行bfs搜到的最后一个点v,一定是直径的一个端点(证明从略),第二次以点v为开头进行bfs,求出的最后一个点,就是直径的另一个端点,记录深度就是我们要求的长度。我这里是使用的bfs+dfs,是一样的,少开一个deep数组,节省一下空间吧……
其实我一开始是不会求的,我以为随便一个叶子节点就可以做端点,交上去WA,当时还好奇感觉没错,结果随便一画,就错了……
注意:这个题里面有重边,我们可以通过重边走回父亲节点,但不可以通过父亲边回到父亲节点,所以原先标记父亲的方式是不可以的,这里改成标记边,在Edge结构体中多加一个vis的变量,标记这个边是否被访问过,那这样重边就能正常访问了。
这个题的数据量比较强,容易爆栈,STL的实用性就不强了,所以尽量使用数组模拟,数组能够开到更大的空间。
感想:感觉这一块的题目真心很爱出错,这个题真是WA了好多次才过的,感觉oj不爱我了……
#include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; #define N 200020 #define M 2000020 struct Edge { int to,nxt,vis; } edge[M]; int head[N],dfn[N],low[N],vis[N],sta[N],id[N]; int tot,all,top,scc,bridge; void addedge(int a,int b,int flag) { edge[tot].to = b; edge[tot].nxt = head[a]; edge[tot].vis = flag; head[a] = tot++; } void tarjan(int u) { vis[u] = 1; dfn[u] = low[u] = ++all; sta[top++] = u; for(int i = head[u]; i != -1; i = edge[i].nxt) { if(edge[i].vis) continue; int v = edge[i].to; edge[i].vis = edge[i^1].vis = 1; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); if(low[v] > dfn[u]) bridge++; } else if(vis[v]) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]) { scc++; int num; do { num = sta[--top]; id[num] = scc; vis[num] = 0; } while(u != num); } } vector<int> tree[N]; int treevis[N],maxdeep; void dfs(int u,int deep) { treevis[u] = 1; maxdeep = max(deep,maxdeep); int len = tree[u].size(); for(int i = 0; i < len; i++) { int v = tree[u][i]; if(!treevis[v]) { dfs(v,deep+1); } } } void init() { memset(vis,0,sizeof(vis)); memset(dfn,0,sizeof(vis)); memset(id,0,sizeof(id)); memset(low,0,sizeof(low)); all = 0; top = 0; scc = 0; bridge = 0; } int que[N]; int getstart() { int front,rear; front = rear = 0; que[rear++] = 1; treevis[1] = 1; while(front != rear) { int now = que[front++]; int len = tree[now].size(); for(int i = 0; i < len; i++) { int nxt = tree[now][i]; if(treevis[nxt]) continue; treevis[nxt] = 1; que[rear++] = nxt; } } return que[--rear]; } int main() { int n,m,a,b; while(~scanf("%d%d",&n,&m)) { if(!n && !m) break; memset(head,-1,sizeof(head)); tot = 0; for(int i = 0; i < m; i++) { scanf("%d%d",&a,&b); addedge(a,b,0); addedge(b,a,0); } init(); tarjan(1); for(int i = 1; i <= scc; i++) { tree[i].clear(); } for(int u = 1; u <= n; u++) { for(int i = head[u]; i != -1; i = edge[i].nxt) { int v = edge[i].to; if(id[u] != id[v]) { int uu = id[u]; int vv = id[v]; tree[uu].push_back(vv); tree[vv].push_back(uu); } } } maxdeep = 0; memset(treevis,0,sizeof(treevis)); int start = getstart(); memset(treevis,0,sizeof(treevis)); dfs(start,1); printf("%d\n",scc-maxdeep); } return 0; }
以上是关于HDU 4612 Warm up(双连通分量缩点+求树的直径)的主要内容,如果未能解决你的问题,请参考以下文章