Advanced Data Structure Topics

1. Tree array

Design binary, binary tree, bit operation, prefix sum and other ideas

lowbit = x & -x

Function: Find the last 1 of the binary number of x

 

 

1.1 Tree array template

def lowbit(x):
    return x &-x
def add (x,d):
    while(x < n) :
        tree[x] +=d
        x+=lowbit(x)
def sum(x):
    ans = 0
    while(x >0):
        ans += tree[x]
        x-=lowbit(x)
        return ans

1.2 Prefixes and applications

1.2.1 Single-point modification, interval query

def lowbit(x):
    return x &-x
def add (x,d):   # 修改元素a[x],a[x]=a[x]+d
    while(x <= N) :
        tree[x] +=d
        x+=lowbit(x)
def sum(x):   # 前缀和思想,返回前缀和sum=a[1]+a[2]+...a[n]
    ans = 0
    while(x >0):
        ans += tree[x]
        x-=lowbit(x)
        return ans

N=1000
tree =[0]*N
a=[0,4,5,6,7,8,9,10,11,12,13]
for i in range(1,11):   # 计算tree数组,即初始化
    add(i,a[i])
print("old:[5,8]=",sum(8)-sum(4))   # 区间和查询
add(5,100)
print("new:[5,8]",sum(8)-sum(4))

 1.2.2 Range modification, range query

# python3.6
# -*- coding: utf-8 -*-
# @Time    : 2023/4/29 9:15
# @Author  : Jin
# @File    : 树状数组.py
# @Software: PyCharm

# python3.6
# -*- coding: utf-8 -*-
# @Time    : 2023/4/29 9:15
# @Author  : Jin
# @File    : 树状数组.py
# @Software: PyCharm

def lowbit(x):
    return x &-x
def add1 (x,d):   # 修改元素a[x],a[x]=a[x]+d
    while(x <= N) :
        tree1[x] +=d
        x+=lowbit(x)
def add2(x,d):
    while(x <= N) :
        tree2[x] +=d
        x+=lowbit(x)
def sum1(x):   # 前缀和思想,返回前缀和sum=a[1]+a[2]+...a[n]
    ans = 0
    while(x >0):
        ans += tree1[x]
        x-=lowbit(x)
    return ans
def sum2(x):
    ans = 0
    while (x > 0):
        ans += tree2[x]
        x -= lowbit(x)
    return ans
N=10010
tree1 =[0]*N
tree2 =[0]*N   #2个树状数组
n,m = map(int,input().split())
old=0
a=[0]+[int(i) for i in input().split()]   # 不用a[0]
for i in range(1,n+1):   # 计算tree数组,即初始化
    add1(i,a[i]-old)   # 差分数组原理初始化
    add2(i,(i-1)*(a[i]-old))
    old=a[i]
for _ in range(m):
    g = [int(i) for i in input().split()]
    if (g[0]==1):  # 区间修改
        L,R,d = g[1],g[2],g[3]
        add1(L,d)     # 第一个树状数组,差分
        add1(R+1,-d)
        add2(L,d*(L-1))  # 第二个树状数组,前缀和
        add2(R+1,-d*R)    # d*R = d*(R+1-1)
    else:    # 区间询问
        L,R = g[1],g[2]
        print(R*sum1(R)-sum2(R)-(L-1)*sum1(L-1)+sum2(L-1))

1.2.3 Reverse order pair problem (merge sort)

def merge_sort(L,R):
    if L < R:
        mid = (L+R)//2
        merge_sort(L,mid)
        merge_sort(mid+1,R)
        merge(L,mid,R)
def merge(L,mid,R):
    global res   # 记录答案
    i=L;j=mid+1;t=0
    while(i<=mid and j<=R):  #归并
        a[i]
        a[j]
        if (a[i]>a[j]):     #4 5 / 2 3   L=0 mid=1,R=3
            b[t]=a[j];t+=1;j+=1;
            res = res+(mid-i+1)  # 记录逆序对数量
        else:
            b[t] = a[i];t += 1;i += 1
    # 其中一个处理完了,把剩下的复制过来,直接整体复制
    # 这里注意区间取值,采用的是整体复制的思想,b是辅助数组
    if i<=mid: b[t:R-L+1]=a[i:mid+1]  # 取不到mid+1
    elif j<=R:b[t:R-L+1]=a[j:]
    # 把排序好的b[]复制回去a[]
    a[L:R+1]=b[:R-L+1]
n= int(input())
a = list(map(int,input().split()))
b = [0]*n
res = 0
merge_sort(0,n-1)
print(res)

1.2.4 Reverse order pair problem (tree array)

def lowbit(x):
    return x&-x
def update(x,d):    # 更新为 +lowbit(x)
    while(x<=n):
        tree[x]+=d
        x+=lowbit(x)
def sum(x):   # 求和为 -lowbit(x)
    ans=0
    while(x>0):
        ans+=tree[x]
        x-=lowbit(x)
    return ans

n=eval(input())
a = [0]+list(map(int,input().split()))  #从a[1]开始
b=sorted(a)  # 从小到大排序
for i in range(n+1):  # 将a更新为排序后的索引元素+1 [0 1 4 2] -> [1 2 4 3],即转为树状数组下标
    a[a.index(b[i])]=i+1
tree = [0]*(n+1)   # 下标从1开始
res =0
for i in range(len(a)-1,0,-1):   # 倒序处理求逆序对
    update(a[i],1) # 更新a[i]+1
    res+=sum(a[i]-1)
print(res)

1.2.5 Discretize elements

def discretization(h):
    b = list(set(h))   # 去重,使得离散化后一样
    b.sort()
    for i in range(len(h)):
        h[i]=b.index(h[i])+1
a=[1,20543,19,376,5460007640,19]
print(a)
discretization(a)
print(a)

2. Line segment tree

2.1 Line segment tree introduction

Based on binary tree ( simulate binary tree by numbers ), dichotomy ( mid=(left+right)//2 ), recursion ( sys.setrecursionlimit(300000) )

Application background:

  • Solve range queries
  • Interval modification problem
  • Multi-interval query for maximum value and interval modification

Segment tree construction

 

 2.2 Tree structure of line segment tree

"""
定义根接点为 tree[1]

通过数组模拟存储,空间开为元素个数的四倍,即 4*N
tree[p]
tree[2p]    #左儿子
tree[2p+1]  #右儿子

"""

2.3 Use the line segment tree to find the maximum number example template, that is, the single-point modification and query operation template

"""
def build(p,pl,pr):  #建树
    if pl=pr:
        tree[p]=
        return
    mid=(pl+pr)//2
    build(2*p,pl,mid)
    build(2*p+1,mid+1,pr)
    tree[p]=max( tree[2*p],tree[2*p+1])   # 这步关键

def update(p,pl,pr,L,R,d):
    if L<=pl and pr <=R:  # 说明当前区间完全包含在要查询的区间中
        tree[p]=d
        return
    # 没有完全包含
    mid = (pl+pr)//2
    if L<=mid:    #查左边
        update(2*p,pl,mid,L,R,d)
    if R>mid:     #查右边
        update(2*p+1,mid+1,pr,L,R,d)
    tree[p]=max(tree[2*p],tree[2*p+1])
    return

def query(p,pl,pr,L,R):  # 查 [L R] 区间最值
    res =-inf
    if L<=pl and pr <=R:  # 说明当前区间完全包含在要查询的区间中
        return tree[p]
    # 没有完全包含
    mid = (pl+pr)//2
    if L<=mid:    #查左边 
        return max(res , query(2*p,pl,mid,L,R))
    if R>mid:     #查右边
         return max(res , query(2*p+1,mid+1,pr,L,R))
    tree[p]=max(tree[2*p],tree[2*p+1])
    return
"""

 Watch out for recursion issues

L≤mid : recursively [pl, mid]

R>mid : recursively [ mid+1 , pr ]

import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)


N=100001
inf=2**50
tree = [0]*4*N  # 初始化树的大小
def build(p,pl,pr):
    if (pl==pr):
        tree[p]=-inf
        return
    mid=(pl+pr)//2
    build(2*p,pl,mid)    #递归构建左孩子 
    build(2*p,mid+1,pr)  #递归构造右孩子
    tree[p]=max(tree[2*p],tree[2*p+1])  #即 push_up操作

def update(p,pl,pr,L,R,d):
    if L<=pl and pr <=R:  # 说明当前区间完全包含在要查询的区间中
        tree[p]=d
        return
    # 没有完全包含
    mid = (pl+pr)//2
    if L<=mid:    #查左边 
        update(2*p,pl,mid,L,R,d)
    if R>mid:     #查右边
        update(2*p+1,mid+1,pr,L,R,d)
    tree[p]=max(tree[2*p],tree[2*p+1])
    return
def query(p,pl,pr,L,R):
    res =-inf
    if L<=pl and pr <=R:  # 说明当前区间完全包含在要查询的区间中
        return tree[p]
    # 没有完全包含
    mid = (pl+pr)//2
    if L<=mid:    #查左边 
        return max(res , query(2*p,pl,mid,L,R))
    if R>mid:     #查右边
         return max(res , query(2*p+1,mid+1,pr,L,R))
    tree[p]=max(tree[2*p],tree[2*p+1])
    return
    

2.4 Lazy-tag technology for interval modification

internal thoughts

 There may be conflicts between multiple interval modifications, which need to be resolved by the push_down() function

2.4.1 Range modification, range query example


import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)


def build(p,pl,pr):
    if (pl==pr):
        tree[p]=a[pl]
        return
    mid=(pl+pr)//2
    build(2*p,pl,mid)    #递归构建左孩子
    build(2*p+1,mid+1,pr)  #递归构造右孩子
    tree[p]=tree[2*p]+tree[2*p+1]  #记录区间和, push_up操作

def update(p,pl,pr,L,R,d):
    if L<=pl and pr <=R:  # 说明当前区间完全包含在要查询的区间中
        addtag(p,pl,pr,d)
        return
    # 没有完全包含
    push_down(p,pl,pr)  # 将懒惰标记传递下去,如果修改区间重叠会有问题
    mid = (pl+pr)//2
    if L<=mid:    #查左边
        update(2*p,pl,mid,L,R,d)
    if R>mid:     #查右边
        update(2*p+1,mid+1,pr,L,R,d)
    tree[p]=tree[2*p]+tree[2*p+1]
    return
def addtag(p,pl,pr,d):   # 给结点p打上标记同时更新tree[p]
    tag[p]+=d
    tree[p]+=d*(pr-pl+1)
def push_down(p,pl,pr):
    if tag[p]>0:  # 有tag标记,需要传递并清空
        mid=(pl+pr)//2
        addtag(2*p,pl,mid,tag[p])   # 传给左孩子
        addtag(2*p+1,mid+1,pr,tag[p])  # 传给右孩子
        tag[p]=0  # 清空当前tag标记

def query(p,pl,pr,L,R):
    if L<=pl and pr <=R:  # 说明当前区间完全包含在要查询的区间中
        return tree[p]
    # 没有完全包含
    push_down(p,pl,pr)  # 如果查询的是标记内部子区间就会有问题!!
    mid = (pl+pr)//2
    res=0
    if L<=mid:    #查左边
        res +=query(2*p,pl,mid,L,R)
    if R>mid:     #查右边
        res +=query(2*p+1,mid+1,pr,L,R)
    return res

n,m = map(int,input().split())
a=[0]+list(map(int,input().split()))
tag=[0]*4*len(a)
tree=[0]*4*len(a)
# 建树
build(1,1,n)
for i in range(m):
    w=list(map(int,input().split()))
    if len(w)==3:  # 区间查询,查询[L,R]区间和
        q,L,R=w
        print(query(1,1,n,L,R))
    else:  # 区间修改,把[L,R]的每个元素加上d
        q,L,R,d=w
        update(1,1,n,L,R,d)

3. Check and set

3.1 Common union check (friends of friends are friends)

3.4.1 Lanqiao Kindergarten

import sys
sys.setrecursionlimit(600000)

from collections import deque
#collections.deque.

def init_set():   # 并查集的初始化
  for i in range(N):
    s.append(i)

def find_set(x):
  if (x!=s[x]):
    s[x] = find_set(s[x])  # 递归查找根节点同时更新更节点
  return s[x]

def merge(x,y):   # 合并并查集
  x = find_set(x)
  y = find_set(y)
  if x!=y:
    s[x]=s[y]

n,m = map(int,input().split())
s = []  # 并查集
N=800000
init_set()
for i in range(m):  # 行
  op,x,y = map(int,input().split())
  if op==1:
    merge(x,y)  # 合并并查集
  if op==2:
    if(find_set(x) == find_set(y)):  print("YES")
    else: print("NO")

3.2 Type and collection (enemy's enemy is friend)

3.2.1 Blue Bridge Detective

import os
import sys

# 请在此输入您的代码
n,m = map(int,input().split())
fa = [i for i in range(2*n+1)]
def find(x):
  if x !=fa[x]:
    fa[x] = find(fa[x])
  return fa[x]

def union(x,y):
  fx = find(x)
  fy = find(y)
  if fx != fy:
    fa[fx] = fy

for i in range(m):
  x,y = map(int,input().split())
  if find(x) == find(y) or find(x+n) == find(y+n):  
    print(x)
    break
  union(x,y+n)   #维护敌人关系
  union(x+n,y)

3.3 With rights and collection

 

# 不能AC,有问题,还没有发现哪点错了
import os
import sys

# 请在此输入您的代码
n,m=map(int,input().split())   # 军人数和命令数
fa=[i for i in range(n+1)]
num=[0 for i in range(n+1)]  #每一个结点到根节点之间的人数
ma=[0 for i in range(n+1)]  #记录根节点后面跟有多少人,最初所有人后面都只有0人跟着
def find(x):
  if x!=fa[x]:  
    t=fa[x]
    fa[x]=find(fa[x])
    num[x]+=num[t]+1  # x与队首的间隔人数等于x与fa[x]之间的间隔人数+fa[x]本身+fa[x]与祖宗结点的间隔人数
  return fa[x]

def merge(x,y):
  x=find(x);y=find(y)
  if x!=y:
    fa[x]=y  #将y的祖宗节点变为x的祖宗节点的父节点
    num[x]=ma[y]  #x原本祖宗节点距离队首间隔的人数为y后面跟着的人数
    ma[y]+=ma[x]+1 #y后面跟着得人数增加x这一列总人数个
for i in range(m):
  a,b,c=map(int,input().split())
  if a==1:
    merge(b,c)
  else:  # 查询x,y之间有多少人
    x=find(b)
    y=find(c)
    ans=-1
    if x==y:  #在一列 # 
      ans=abs(num[x]-num[y])-1
    print(ans)

4. Monotonic queue

Use deque to achieve

4.1 MAX to find the most value 

import os
import sys

# 请在此输入您的代码
from collections import deque
def findMaxdiff(n,k,a):
  q1,q2=deque(),deque()   # 建两个单调队列
  res=-sys.maxsize
  for i in range (n):  # 遍历每一个元素
    while q1 and a[q1[-1]]>=a[i]:  #q1有元素且最后一个元素都大于a[i]
      q1.pop()
    q1.append(i)
    while q1 and i-q1[0]>=k:
      q1.popleft()
    Fi=a[q1[0]]

    while q2 and a[q2[-1]]<=a[i]:  #q1有元素且最后一个元素都大于a[i]
      q2.pop()
    q2.append(i)
    while q2 and i-q2[0]>=k:
      q2.popleft()
    Gi=a[q2[0]]
    
    #计算最大差值 
    res=max(res,Gi-Fi)
  return  res


n,k=map(int,input().split())
A=list(map(int,input().split()))
print(findMaxdiff(n,k,A))

Guess you like

Origin blog.csdn.net/weixin_52261094/article/details/130436858