Aizu 2677 Breadth-First Search by Foxpower(LCA)

题意:给一棵根为1的无权树,要按BFS的顺序去走它(如果深度一样,优先走父节点先走的,如果父节点也一样,优先走数字小的),问一共要走多少步(一条边为一步)。


思路:LCA+BFS。按BFS的顺序走,如果出队顺序为1,a,b,c,d……,即从1走到a,从a走到b,从b走到c,从c走到d……。在树上走,两点间的最短距离显然为:d[u]+d[v]-d[LCA(u,v)] (d[x]为x的深度,即x离根的距离)

注意点:

  1. 最终结果会超int
  2. 优先走数字小的,用邻接表加边的话要倒序加边
  3. tarjan算法为保证询问完整性需要双向加边


/*************************************************************************
     File Name: Aizu2677.cpp
     ID: obsolescence
     BLOG: http://blog.csdn.net/obsolescence
     LANG: C++ 
     Mail: 384099319@qq.com 
     Created Time: 2016年07月29日 星期五 21时13分37秒
 ************************************************************************/
#include<bits/stdc++.h>
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
#define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();++it)
#define Abs(x,y) ((x)>(y)?((x)-(y)):((y)-(x)))
#define ll long long
#define Mem0(x) memset(x,0,sizeof(x))
#define Mem1(x) memset(x,-1,sizeof(x))
#define MemX(x) memset(x,0x3f,sizeof(x))
#define pb push_back
using namespace std;
const int N=1e5+10;
int n,top,pre[N],d[N],x[N];
bool vis[N],root[N];
ll ans;
vector<int> qes[N];
struct Edge{
  int to;
  Edge *next;
}*head[N],e[N<<1];

void addEdge(int from,int to) {
  Edge *p=&e[++top];
  p->to=to,p->next=head[from],head[from]=p;
}

int find(int x) {
  return x==pre[x]? x:pre[x]=find(pre[x]);
}

void Union(int x,int y) {
  x=find(x),y=find(y);
  if (x!=y) pre[y]=x;
}

void TarjanLca(int u) {
  for (Edge *p=head[u]; p; p=p->next) {
    TarjanLca(p->to);
    Union(u,p->to);
  }
  vis[u]=1;
  for (auto i:qes[u])
    if (vis[i])
      ans+=d[u]+d[i]-2*d[find(i)];
}

void bfs() {
  queue<int> q;
  q.push(1);
  int s,t=1;
  while (!q.empty()) {
    s=q.front();
    q.pop();
    qes[t].pb(s);
    qes[s].pb(t);
    for (Edge *p=head[s]; p; p=p->next) {
      d[p->to]=d[s]+1;
      q.push(p->to);
    }
    t=s;
  }
}

void init() {
  Mem0(root),Mem0(vis),Mem0(d),Mem0(head);
  for (int i=0; i<=n; ++i)
    pre[i]=i,qes[i].clear();
  top=ans=0;
}

int main() {
  int i;
  while (~scanf("%d",&n)) {
    init();
    for (i=2; i<=n; ++i)
      scanf("%d",x+i);
    for (i=n; i>=2; --i) {
      addEdge(x[i],i);
      root[i]=1;
    }
    bfs();
    for (i=1; i<=n; ++i)
      if (!root[i]) {
        TarjanLca(i);
        break;
      }
    printf("%lld\n",ans);
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值