给一个根为1的无权树,然后它要人工BFS这棵树,就是按BFS的顺序去走它,问一共要走多少步,假设一条边走一步
首先就BFS这个树,然后根据出队顺序得到我们走的顺序,假设为a,b,c,d,e,f,,,,然后,呃,a肯定是1,所以我们就是从1走到b,然后b到c,c到d,
所以我们要求他们之间的距离,树上两点的距离显然就是,d[u]+d[v]-2*d[lca(u,v)]。d[u]表示它的深度(走到根节点的路径长度),lca表示这两个点的最近公共祖先。
这个显然就是tarjan离线求lca了。讲解网上很多,大概就是根据dfs序边求边搞。
我这里只说一个问题就是,我们对于所有的查询,也是以树的形式保存下来了,在遍历完这个子树之后我们要进行对应的查询,比如u,v吧,我们把u子树操作完之后,就要查询v,但是这时候v可能访问或者没访问,如果访问了我们肯定就统计下来了,没访问呢,这就涉及到我们建这个树时是建一条边还是两条边了,不太清楚网上那些建一条边的是怎么解决的,可能有特殊的建边技巧或者数据有某种性质。反正我看到的解决办法就是建双向边。
它那个利用的是建一个vis数组表示是否完全访问,然后在处理完这个树的所有子树时就标记为true,然后我想说的一个问题它就解决了,就是假如v是u的子树里的,那么在处理v时,v操作完之后u的标记还是false,因为u还没操作完,但是u操作完之后v肯定是true的,所以这时就计算了。当然,假如不在一个子树就肯定是晚操作的那个节点进行计算。
但是我想节省些空间就没有建立vis数组,是通过判断它的并查集的根是否为0(初始值)来判断的,也是建立的双向边,这样假如v不在u的子树里肯定是没问题的,访问u时v还没操作,然后v操作那里u已经操作结束了,然后就会计算,但是如果v是u的子树,v操作完之后,因为u已经开始操作了,所以它的并查集已经有根了,然后就会计算一次,而在u操作完之后也会计算一次,后来想的一个折中的办法就是一开始时只建立一条单向边,假设为u到v,那么我在u操作完时要计算,假如v已经被处理了,肯定直接计算就可以,但是假如v没被处理,那就加一条v到u的边,这样后面再处理v时肯定就会计算u和v了,然后就解决了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
#define ll long long
#define maxn 100005
vector<int> tree[maxn];
vector<int> query[maxn];
int father[maxn];
int depth[maxn];
ll ans = 0;
int findSet(int x)//路径压缩
{
if (x == father[x])
return x;
else
return father[x] = findSet(father[x]);
}
void unionSet(int x, int y)//把y并到x中去
{
int fx = findSet(x);
int fy = findSet(y);
if (fy == fx)
return;
father[fy] = fx;
}
void bfs()
{
queue<int> Q;
Q.push(1);
int last = 0;
int t;
while (!Q.empty())
{
t = Q.front();
if (last != 0)
{
query[last].push_back(t);
}
last = t;
Q.pop();
for (unsigned int i = 0; i < tree[t].size(); ++i)
{
Q.push(tree[t][i]);
}
}
}
void Tarjan_LCA(int u)
{
father[u] = u;
int t;
for (unsigned int i = 0; i < tree[u].size(); ++i)
{
t = tree[u][i];
depth[t] = depth[u] + 1;
Tarjan_LCA(t);
unionSet(u, t);
}
for (unsigned int i = 0; i < query[u].size(); ++i)
{
t = query[u][i];
if (father[t] != 0)
{
//printf("sum %d %d\n", u, t);
ans += (ll)depth[u] + (ll)depth[t] - 2 * (ll)depth[findSet(t)];
}
else
{
query[t].push_back(u);
}
}
}
int main()
{
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout);
int n;
scanf("%d", &n);
int tmp;
for (int i = 2; i <= n; ++i)
{
scanf("%d", &tmp);
tree[tmp].push_back(i);
}
bfs();
/*for (int i = 1; i <= n; ++i)
{
for (unsigned int j = 0; j < query[i].size(); ++j)
printf("%d ", query[i][j]);
printf("\n");
}*/
Tarjan_LCA(1);
/*for (int i = 1; i <= n; ++i)
printf("%d ", depth[i]);
printf("\n");*/
printf("%lld\n", ans);
//system("pause");
//while (1);
return 0;
}