Kth Ancestor 第k个祖先问题

题目出处

这道题目出自hackerrank的8月月赛的第三题。

题目大意

先给出一棵树

之后有三种操作分别为:加边,查询,和删除一个节点

查询的时候要给出任意节点x的第k个祖先

每组数据有t个case

每个case边(P)的数量小于等于10^5

每个case的操作的数量(Q)小于等于10^5

 

题目分析

一开始拿到这个题目的时候被搞得一头雾水,如果采用普通的暴力的办法,每一个查询需要O(P),总体的复杂度就变成了O(Q*P),铁定TLE…

思考了三天没有什么想法然后搜了一下,发现了这个:

Level ancestor problem

研读了一番之后发现了使用一个神奇的数据结构,使得每一次的查询可以降为long(P)的复杂度,这样问题就迎刃而解了。

这个奇特的数据结构,我这样描述:

对树进行DFS,记录下每一条路径e[i]。

之后开一个node[i]标记每个节点所在的边号(index),以及在该边的深度(depth)还有该点的“父节点”(father 该点所在边的起点e[node[i].index][0]的父节点)。

那么查询的时候Q(x,k)就等于:

k <= node[x].depth 时 return e[node[x].index][node[x].depth-k]

k > node[x].depth 时 return Q(node[x].father,k-1)

 

 

 

之后就是一系列的边界描述,不再赘述了。

第一次写出来的这个代码十分之丑陋,大家见笑了

python3写的

Level ancestorclass node:
    def __init__(self,depth = 0,father = -1,index = -1,mark = -2):
        self.depth = depth
        self.father = father
        self.index = index
        self.mark = mark
path = []
nodes = []

MAXN = 100000+5

def NEW(x,y):
    path.append([y,x])
    l = len(path)-1
    nodes[y] = node(0,-1,l,-1)
    nodes[x] = node(0,y,l,1)

def CON(x,y):
    if (  nodes[y].mark == 1 ):
        nodes[x] = node(nodes[y].depth+1,nodes[y].father,nodes[y].index,1)
        nodes[y].mark = 0
        path[nodes[y].index].append(x)
    elif(( nodes[y].mark == -1 ) & ( len(path[nodes[y].index]) == 1 ) ):
        nodes[x] = node(nodes[y].depth+1,nodes[y].father,nodes[y].index,1)
        path[nodes[y].index].append(x)    
    else:
        ADD(x)
        l = len(path)-1
        nodes[x] = node(0,y,l,1)

def ADD(x):
    path.append([x])

def update(x,y):
    if ( ( nodes[x].mark == -2 ) & ( nodes[y].mark == -2 ) ):
        NEW(x,y)
    elif ( ( nodes[x].mark == -2 ) & ( nodes[y].mark != -2 ) ):
        CON(x,y)
    elif ( ( nodes[x].mark != -2 ) & ( nodes[y].mark != -2 ) ):
        nodes[x].father = y
    elif ( ( nodes[x].mark != -2 ) & ( nodes[y].mark == -2 ) ):
        ADD(y)
        nodes[y] = node(0,-1,len(path)-1,1)
        nodes[x].father = y

def DEL(x):
    path[nodes[x].index].remove(x)
    nodes[x] = node()

def LOA(x,k):
    if ( x == 0 ):
        return 0;
    if ( nodes[x].mark == -2 ):
        return 0
    if (nodes[x].depth >= k):
        lt = nodes[x].depth - k
        if ( path[nodes[x].index][0] == 0 ):
            lt = lt+1
        return path[nodes[x].index][lt]
    if ( nodes[x].depth < k ):
        if ( ( nodes[x].father == -1 ) or ( nodes[x].father == 0 ) ):
            return 0
        t = LOA(nodes[x].father,k-nodes[x].depth-1)
        return t

def INIT():
    global path
    global nodes
    path = []
    nodes = [node() for i in range(0,MAXN)]


def build():
    n = int(input())
    for i in range(0,n):
        x,y = input().split(' ')
        x = int(x)
        y = int(y)
        update(x,y)
    #print(path)

def Q():
    n = int(input())
    for i in range(0,n):
        lt = input().split(' ')
        if ( lt[0] == '0' ):
            update(int(lt[2]),int(lt[1]))
            #print(path)
        if ( lt[0] == '1' ):
            DEL(int(lt[1]))
        if ( lt[0] == '2' ):
            print(LOA(int(lt[1]),int(lt[2])))

t = input()
t = int(t)
for i in range(0,t):
    INIT()
    build()
    Q()



以后争取做到学会了就记录下来,这个代码贴给后人鄙视吧

转载于:https://www.cnblogs.com/qoshi/p/3327929.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值