SCU 1187: Closest Common Ancestors

题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=1187


题目大意:

LCA模板题


算法:

我写LCA从来都是用倍增,倍增是在线的,但是传说比离线的tarjan慢1/3。

倍增依据的思想是ST算法,附一段网上的介绍:

dp[i][j]表示区间[i,i+2^j-1]的最小值,则
dp[i][0]=a[i]
dp[i][j]=min{dp[i][j-1],dp[i+2^(j-1)][j-1]}
这样可以得到所有的dp[i][j]

如果询问区间为[s,t],则只需要取k=(int)log2(t-s+1)
RMQ[s,t]=min{dp[s][k],dp[t-2^k+1][k]}
这时候预处理的算法复杂度仅为O(nlogn)
而回答问题仍然是O(1)的复杂度
实际操作的时候,我们不一定用log来计算k,也可以通过二分查找。


在倍增法求LCA的过程中,

go[i][j]数组表示由i点向上跳j层到达的根节点。

预处理go数组,可以dfs的时候由根节点向子节点传递一下。

求解LCA时,先让两个点位于同一层上,

然后再二分地让他们跳到LCA下面一层的位置,就可以求出LCA。


代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

int go[11000][17],dep[11000],hash[11000],p[11000],dis[11000][17];
vector<int>map[11000];

void dfs(int u)
{
  for(int t=0; t<map[u].size(); t++)
    {
      int v=map[u][t];
      dep[v]=dep[u]+1;
      go[v][0]=u;
      for(int k=1; (1<<k)<=dep[v]; k++)
        {
          go[v][k]=go[go[v][k-1]][k-1];
        }
      dfs(v);
    }
}

int LCA(int u,int v)
{
  if(dep[v]>dep[u])
    swap(u,v);
  int jmp=dep[u]-dep[v];
  for(int k=16; k>=0; k--)
    if(jmp>>k&1)
      {
        u=go[u][k];
      }
  if(u==v)
    return u;
  int k;
  for(k=16; k>=0; k--)
    if(((1<<k)<dep[u])&&go[u][k]!=go[v][k])
      {
        u=go[u][k];
        v=go[v][k];
      }
  return go[u][0];
}

int main()
{
  int n,m;
  while(~scanf("%d",&n))
    {
        memset(p,-1,sizeof(p));
      memset(go,-1,sizeof(go));
      for(int i=0; i<n; i++)
        {
            int u,v,num;
          scanf("%d:(%d)",&u,&num);
          u--;
          map[u].clear();
          while(num--)
          {
     scanf("%d",&v);
     v--;
          map[u].push_back(v);
          p[v]=u;
          }
        }
      for(int i=0; i<n; i++)
        {
          if(p[i]==-1)
          {
       dep[i]=0;
            dfs(i);
          }
        }
    memset(hash,0,sizeof(hash));
    scanf("%d\n",&m);
      while(m--)
        {
          int u,v;
          scanf("(%d,%d)",&u,&v);
          getchar();
          u--;
          v--;
          hash[LCA(u,v)]++;
        }
      for(int i=0;i<n;i++)
      {
          if(hash[i])
          {
            printf("%d:%d\n",i+1,hash[i]);
          }
      }
    }
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值