树状数组-算法日常练习记录

树状数组可以通过下面这个网站进行学习:

树状数组 - OI Wiki (oi-wiki.org)

#130. 树状数组 1 :单点修改,区间查询

#130. 树状数组 1 :单点修改,区间查询 - 题目 - LibreOJ (loj.ac)

"""
https://loj.ac/p/130
单点修改,区间查询
"""


def lowbit(x: int):
    return x & -x


def getsum(x):
    ans = 0
    while x > 0:
        ans += c[x]
        x -= lowbit(x)
    return ans


def add(pivot, v):
    while pivot <= n:
        c[pivot] += v
        pivot += lowbit(pivot)


if __name__ == '__main__':
    n, q = map(int, input().strip().split(" "))
    lst = [-1] + list(map(int, input().strip().split(" ")))  # 方便下标操作
    ops = [(map(int, input().strip().split(" "))) for _ in range(q)]
    c = [0] * (n + 10)  # 建立
    res = []
    # 建树
    for i in range(1, n + 1):
        add(i, lst[i])
    del lst
    for op in ops:
        t, x, y = op
        if t == 1:
            add(x, y)
        else:
            res.append(getsum(y) - getsum(x - 1))
    for i in res:
        print(i)

#131. 树状数组 2 :区间修改,单点查询

#131. 树状数组 2 :区间修改,单点查询 - 题目 - LibreOJ (loj.ac)

"""
https://loj.ac/p/130
单点修改,区间查询
"""


def lowbit(x: int):
    return x & -x


def getsum(x):
    ans = 0
    while x > 0:
        ans += c[x]
        x -= lowbit(x)
    return ans


def add(pivot, v):
    while pivot <= n:
        c[pivot] += v
        pivot += lowbit(pivot)


if __name__ == '__main__':
    n, q = map(int, input().strip().split(" "))
    lst = [0] + list(map(int, input().strip().split(" ")))  # 方便下标操作
    ops = [list(map(int, input().strip().split(" "))) for _ in range(q)]
    c = [0] * (n + 10)  # 建立
    # O(n)版本,利用前缀和
    for i in range(1, n + 1):
        c[i] = lst[i] - lst[i - lowbit(i)]
    for i in range(n,0,-1):
        lst[i] = lst[i] - lst[i - 1]
    res = []
    # 建树
    # O(nlogn)版本
    # for i in range(1, n + 1):
    #     add(i, lst[i])
    for op in ops:
        type, t = op[0], op[1:]
        if type == 1:
            x, y, z = t
            add(x, z)
            add(y + 1, -z)
        else:
            t = t[0]
            print(getsum(t))

#132. 树状数组 3 :区间修改,区间查询

#132. 树状数组 3 :区间修改,区间查询 - 题目 - LibreOJ (loj.ac)

"""
https://loj.ac/p/132
区间加区间和
"""


def lowbit(x):
    return x & -x


def add(pivot, x, tr, n):
    while pivot <= n:
        tr[pivot] += x
        pivot += lowbit(pivot)


def getsum(pivot, tr):
    ans = 0
    while pivot > 0:
        ans += tr[pivot]
        pivot -= lowbit(pivot)
    return ans


def getAllSum(x, tr_d, tr_di):
    return getsum(x, tr_d) * (x + 1) - getsum(x, tr_di)


n, q = map(int, input().strip().split(" "))
lst = [0] + list(map(int, input().strip().split(" ")))
ops = [list(map(int, input().strip().split(" "))) for _ in range(q)]
tr_d = [0] * (n + 10)
tr_di = [0] * (n + 10)
# 构造差分数组
for i in range(n, 0, -1):
    lst[i] = lst[i] - lst[i - 1]
# 初始化tr_d & tr_di 时间复杂度O(nlogn)
for i in range(1, n + 1):
    add(i, lst[i], tr_d, n)
    add(i, lst[i] * i, tr_di, n)
# 开始操作
for op in ops:
    op_type, t = op[0], op[1:],
    if op_type == 1:
        l, r, x = t
        add(l, x, tr_d, n)
        add(r + 1, -x, tr_d, n)
        add(l, x * l, tr_di, n)
        add(r + 1, -x * (r + 1), tr_di, n)
    else:
        l, r = t
        res = getAllSum(r, tr_d, tr_di) - getAllSum(l - 1, tr_d, tr_di)
        print(res)

1265. 数星星

1265. 数星星 - AcWing题库

"""
https://www.acwing.com/problem/content/1267/
"""


def lowbit(x):
    return x & -x


def add(pivot, x, tr, n):
    while pivot <= n:
        tr[pivot] += x
        pivot += lowbit(pivot)


def getsum(pivot, tr):
    ans = 0
    while pivot:
        ans += tr[pivot]
        pivot -= lowbit(pivot)
    return ans


n = int(input())
lst = [list(map(int, input().split(" "))) for _ in range(n)]
N = 32010
tr = [0] * (N + 10)
res = []
ans = [0] * n
for x, y, in lst:
    add(x + 1, 1, tr, N)
    # 算出每一个星星的星级
    res.append(getsum(x + 1, tr) - 1)
# 整理答案
for r in res:
    ans[r] += 1
# 输出答案
for a in ans:
    print(a)

LCR 170. 交易逆序对的总数

LCR 170. 交易逆序对的总数 - 力扣(LeetCode)

"""
https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
求逆序对
"""


def re_lst(lst):
    """离散化数组"""
    tem = [(pivot, i) for pivot, i in enumerate(lst)]
    tem.sort(key=lambda x: x[1])
    for i in range(len(tem)):
        lst[i] = tem[i][0] + 1  # 从1开始,方便树状数组


def lowbit(x):
    return x & -x


def add(pivot, x, tr, n):
    while pivot <= n:
        tr[pivot] += x
        pivot += lowbit(pivot)


def getSum(pivot, tr):
    ans = 0
    while pivot:
        ans += tr[pivot]
        pivot -= lowbit(pivot)
    return ans


def getPairs(lst):
    """顺序对"""
    n = len(lst)
    tr = [0] * (n + 10)
    ans = 0
    for p, i in enumerate(lst):
        add(i, 1, tr, n)
        ans += getSum(i, tr) - 1
    return ans


def getReversePairs(lst: list):
    n = len(lst)
    tr = [0] * (n + 10)
    ans = 0
    for i in range(n - 1, -1, -1):
        add(lst[i], 1, tr, n)
        ans += getSum(lst[i], tr) - 1
    return ans


lst = [4, 3, 2, 1]
re_lst(lst)
print(lst)
# 先求顺序对
t = getPairs(lst)
print(t)
# 求逆序对
print(getReversePairs(lst))

1215. 小朋友排队

1215. 小朋友排队 - AcWing题库

"""
https://www.acwing.com/problem/content/1217/
使用树状数组解决该问题
"""


def re_lst(lst):
    """离散化数组"""
    tem = [(pivot, i) for pivot, i in enumerate(lst)]
    tem.sort(key=lambda x: x[1])
    for i in range(len(tem)):
        lst[i] = tem[i][0] + 1  # 从1开始,方便树状数组


def lowbit(x):
    return x & -x


def add(pivot, x, tr, n):
    while pivot <= n:
        tr[pivot] += x
        pivot += lowbit(pivot)


def getSum(pivot, tr):
    ans = 0
    while pivot:
        ans += tr[pivot]
        pivot -= lowbit(pivot)
    return ans


def getPairs(lst):
    """顺序对"""
    n = len(lst)
    tr = [0] * (n + 10)
    for p, i in enumerate(lst):
        add(i, 1, tr, n)
        num[i] += getSum(n,tr) - getSum(i, tr) # 关键步骤
    return


def getReversePairs(lst: list):
    n = len(lst)
    tr = [0] * (n + 10)
    for i in range(n - 1, -1, -1):
        add(lst[i], 1, tr, n)
        num[lst[i]] += getSum(lst[i], tr) - 1 # 关键步骤
    return


n = int(input())
lst = list(map(int, input().strip().split(" ")))
re_lst(lst)
num = [0] * (n + 10)  # 记录交换次数的数组
# 计算逆序
getReversePairs(lst)
# 同上
getPairs(lst)
# 计算结果
res = 0
for v in num:
    res += ((1 + v) * v) // 2
print(res)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值