来源
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<23−1那么这就以为着,当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 帮助
恳请大佬留言指正,感激不尽。