【算法】简单的线段树维护·学校暑假ACM校内模拟赛·Segtree Master

来源

2022年暑假,学校模拟赛。

时间限制与内存限制

5秒 256MB

题目描述

对于数组a1,a2,…,an 请维护一种数据结构:(请看下方截图)
在这里插入图片描述
在这里插入图片描述

样例输入2:

4 20
69 52 16 56 
1 2 2 3
2 1 47
2 4 96
3 3
3 1
3 2
3 1
2 4 8
1 2 2 1
1 2 4 2
3 1
1 2 4 3
3 4
3 4
2 2 29
2 1 84
2 1 90
1 1 4 2
2 4 5
3 4

样例输出2:

16
47
17
47
47
1
1
5

在这里插入图片描述

思路:

并没有什么特别难的地方,这道题考察的是一个线段数的维护,那么记录每次修改的v值进入相应的线段,然后查询的时候再取出来即可,那么复杂度即可将为 l o g n log n logn级别,这是因为使用了树状来维护,修改和查询这个线段树都是一样的复杂度。

另外,我们使用a数组来保存,刚刚开始读入的数据,然后再使用线段树来计算得到最终的答案。(具体请看代码)

我们使用life数组来保存这个a[i]节点是什么时候被修改过的(比如第1年又被修改),那么作用于a[i]的数据,只能是life和life之后的v累乘作用(即第一年,第二年…,第0年绝对不行,还没出生呢),相当于,一个表格,两次使用。

这里有一个非常牛逼的地方,可以大大加快代码的运行速度。
那就是,一定要观察,我们注意到 v < 1 0 9 v<10^9 v<109 a i < 2 3 − 1 ai<2^3-1 ai<231那么这就以为着,当v累乘特别大的时候,直接返回0。不做了,通过这种优化,又一下子将代码的复杂度降低了。Nice!

代码:

//这是TE版本的程序
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
// 300000
#define N 3 * 100001
int n, q;
unsigned long long a[N];
int life[N];
int nowLife = 0;
unsigned long long total = 1;
using namespace std;
typedef unsigned long long ull;
struct tree {
    struct node {
        int L, R;
        map<int, ull> mTime;
    } nd[N * 4];

    void build(int p, int L, int R) {
        nd[p] = {L, R};
        if (L == R)
            return;
        int mid = L + R >> 1;
        build(p << 1, L, mid);
        build(p << 1 | 1, mid + 1, R);
    }

    void modify(int p, int L, int R, int v) {
        if (nd[p].L >= L && nd[p].R <= R) {
            if (nd[p].mTime.find(nowLife) != nd[p].mTime.end()) {
                nd[p].mTime[nowLife] *= v;
            } else {
                nd[p].mTime[nowLife] = v;
            }
            return;
        }
        int mid = nd[p].L + nd[p].R >> 1;
        if (L <= mid) {
            modify(p << 1, L, R, v);
        }
        if (R > mid) {
            modify(p << 1 | 1, L, R, v);
        }
    }

    void query(int p, int x) {  //计算x节点上的v值
        for (auto it = nd[p].mTime.begin(); it != nd[p].mTime.end(); it++) {
            if (it->first >= life[x - 1]) {
                total *= it->second;
                if (total == 0 || total > 2147483647) {
                    total = 0;
                    return;
                }
            }
        }
        if (nd[p].L == nd[p].R)
            return;
        int mid = nd[p].L + nd[p].R >> 1;
        if (x <= mid) {
            query(p << 1, x);
        } else {
            query(p << 1 | 1, x);
        }
    }

    void resetTree() {
        for (int i = 0; i < n; i++) {
            total = 1;
            this->query(1, i + 1);
            if (total == 0) {
                total = a[i + 1] + 1;
            }
            a[i] = a[i] / total;
        }
        for (int i = 0; i <= 4 * n; i++) {
            this->nd[i].mTime.clear();
        }
    }
} ST;
int main() {
    freopen("81In.txt", "r", stdin);
    freopen("83Out.txt", "w", stdout);
    scanf("%d %d", &n, &q);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &a[i]);
        life[i] = 0;
    }
    //构造线段树
    ST.build(1, 1, n);
    int op;
    for (int i = 0; i < q; i++) {
        scanf("%d", &op);
        if (op == 1) {  //[l,r]修改v
            int l, r, v;
            scanf("%d %d %d", &l, &r, &v);
            ST.modify(1, l, r, v);
        } else if (op == 2) {  //单点修改
            int x, v;
            scanf("%d %d", &x, &v);
            nowLife++;
            a[x - 1] = v;
            life[x - 1] = nowLife;
        } else {  //查询
            int x;
            scanf("%d", &x);
            total = 1;
            ST.query(1, x);
            if (total == 0) {
                total = a[x - 1] + 1;
            }
            unsigned long long ans = a[x - 1] / total;
            printf("%lld\n", ans);
            // cout << "totalLife: " << nowLife << endl;
        }
        if (nowLife % 20000) {
            // ST.resetTree();
        }
    }
    return 0;
}

这个代码,在我自己机器上测试,时间小于5s,在学校测,5.3s结束。可恶。
就是有三个点过不去。
我想一定会有更好的思路,但是,先把这道题,先放在这里趴。晚安。


更多有趣题目正在悄悄袭来o(〃^▽^〃)o

2022年7月14日补充:
一个python版本的简单线段数:
这个程序,查询,插入,修改,都是Log(N)级别,N是a数组长度。
最后还是超时。
因为我用了python的高精度功能。
另外,python真的太好用了,我爱python(逃)

import math


class Tree:
    node = dict()  # node[x]={L:,R:,V:Number}

    def buildTree(self, p: int, l: int, r: int):
        self.node[p] = {"L": l, "R": r, "V": 1}
        if l == r:
            return
        mid = (l + r) // 2
        self.buildTree(2 * p, l, mid)  # 建造树的时候,把Mid加入进了左子树
        self.buildTree(2 * p + 1, mid + 1, r)
        pass

    def queryTree(self, p: int, x: int, num):
        num = num // self.node[p]["V"]
        if num == 0:
            return 0
        if self.node[p]["L"] == self.node[p]["R"]:
            return num
            pass
        mid = (self.node[p]["L"] + self.node[p]["R"]) // 2
        if x <= mid:
            return self.queryTree(p * 2, x, num)
        else:
            return self.queryTree(p * 2 + 1, x, num)

    def modifyTree(self, p: int, l: int, r: int, v: int):
        if l <= self.node[p]["L"] and self.node[p]["R"] <= r:
            self.node[p]["V"] *= v
            return
        mid = (self.node[p]["L"] + self.node[p]["R"]) // 2
        if l <= mid:
            self.modifyTree(p * 2, l, r, v)
        if r > mid:
            self.modifyTree(p * 2 + 1, l, r, v)

    def totalNode(self, x: int, p=1):
        if self.node[p]["L"] == self.node[p]["R"]:
            return self.node[p]["V"]
        mid = (self.node[p]["L"] + self.node[p]["R"]) // 2
        if x <= mid:
            return self.totalNode(x, p * 2) * self.node[p]["V"]
        else:
            return self.totalNode(x, p * 2 + 1) * self.node[p]["V"]
        pass

    def modifyNode(self, x: int, v: int):
        a[x - 1] = v * self.totalNode(x)
        pass


if __name__ == '__main__':
    st = Tree()
    n, q = map(int, input().split())
    a = input().split()
    for i, it in enumerate(a):
        a[i] = int(it)

    st.buildTree(1, 1, n)
    # op=1 l r v | op=2 x v | op=3 x
    for i in range(0, q):
        inpu = input()
        opm = int(inpu[0])
        if opm == 1:
            op, l, r, v = map(int, inpu.split())
            st.modifyTree(1, l, r, v)
        elif opm == 2:
            op, x, v = map(int, inpu.split())
            st.modifyNode(x, v)
        else:
            op, x = map(int, inpu.split())
            print(st.queryTree(1, x, a[x - 1]))
    pass

    # 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助

恳请大佬留言指正,感激不尽。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值