高级数据结构基础代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
树状数组代码:
 
//前缀和
int query(int x){
    int ans = 0;
    for(; x; x -= x& -x) ans += C[x];
    return ans;
}
 
//单点修改
void modify(int x, int y){
    for(; x <= N; x += x & -x)
        c[x] += y;
}
 
//树状数组区改区查
const int SIZE = 100010;
int a[SIZE], n, m;
long long C[2][SIZE], sum[SIZE];
long long query(int k, int x){
    long long ans = 0;
    for(; x; x -= x & -x)
        ans += C[k][x];
    return ans;
}
void modify(int k, int x, int y){
    for(; x <= n; x += x & -x)
        C[k][x] += y;
}
 
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        sum[i] = sum[i-1] + a[i];
    }
    while(m--){
        char op[2];
        int l,r,d;
        scanf("%s%d%d", op, &l, &r);
        if(op[0] == 'C'){
            scanf("%d", &d);
            modify(0, l, d);    modify(0, r+1, -d);         //0代表C0
            modify(1, l, l*d);  modify(1, r+1, -(r+1)*d);   //1代表C1
        }
        else{//query
            long long ans = sum[r] + (r+1)*query(0, r) - query(1,r);
            ans -= sum[l-1] + l * query(0, l-1) - query(1, l-1);
            printf("%lld\n", ans);
        }
    }
}
 
 
//线段树的建立
struct SegmentTree{
    int l, r;
    int dat;
} t[SIZE * 4];
 
void build(int p, int l, int r){
    t[p].l = l, t[p].r = r; //节点p代表区间[l,r]
    if(l == r){ //叶节点
        t[p].dat = a[l];
        return;
    }
    int mid = (l+r)/2; //折半
    build(p*2, l, mid); //左子节点
    build(p*2+1, mid+1, r) //右子节点
    t[p].dat = max(t[p*2].dat, t[p*2+1].dat); //由下往上维护RMQ性质
}
build(1,1,n);
//A={3,6,4,8,1,2,9,5,7,0}
 
//线段树的单点修改
void modify(int p, int x, int v){
    if(t[p].l == t[p].r) { //叶节点
        t[p].dat = v;
        return;
    }
    int mid = (t[p].l + t[p].r)/2;
    if(x <= mid) modify(p*2, x ,v) //x属于左子树区间
    else modify(p*2+1, x, v) //x属于右子树区间
    t[p].dat = max(t[p*2].dat, t[p*2+1].dat); //由下往上维护RMQ性质
}
modify(1,x,v);
//modify(1,7,1)
 
//线段树的区间查询
int query(int p, int l, int r){
    if(l <= t[p].l && r >= t[p].r) return t[p].dat; //完全覆盖
    int mid = (t[p].l + t[p].r)/2;
    int val = -(1<<30); //负极大数
    if(l <= mid) val = max(val, query(p*2, l, r)); //左子节点有覆盖
    if(r  > mid) val = max(val, query(p*2+1, l, r)); //右子节点有覆盖
    return val;
}
 
cout << query(1,l,r) << endl; //调用入口
 
//带Lazy Tag的线段树
struct SegmentTree{
    int l, r;
    long long sum, tag;
    #define l(x) tree[x].l
    #define r(x) tree[x].r
    #define sum(x) tree[x].sum
    #define tag(x) tree[x].tag
} t[SIZE * 4];
int a[SIZE], n, m;
 
void build(int p, int l, int r){
    l(p) = l, r(p) = r; //节点p代表区间[l,r]
    if(l == r){ //叶节点
        sum(p) = a[l];
        return;
    }
    int mid = (l+r)/2; //折半
    build(p*2, l, mid); //左子节点
    build(p*2+1, mid+1, r) //右子节点
    sum(p) = sum(p*2) + sum(p*2+1);
}
 
void spread(int p){
    if(tag(p)) { //节点p有标记
        sum(p*2) += tag(p)*(r(p*2) - l(p*2)+1); //更新左子节点
        sum(p*2+1) += tag(p)*(r(p*2+1) - l(p*2+1)+1); //更新右子节点
        tag(p*2) += tag(p); //在左子节点更新标记
        tag(p*2+1) += tag(p); //在右子节点更新标记
        tag(p) = 0; //本节点标记清除
    }
}
 
//区间修改
void modify(int p, int l, int r, int v){
    if(l <= l(p) && r >= r(p)) { //完全覆盖
        sum(p) += (long long)v * (r(p)-l(p)+1); //更新节点信息
        tag(p) += v; // 更新节点的标记信息
        return;
    }
    spread(p); //如果区间未完全被覆盖,则往下传递标记信息
    int mid = (l(p) + r(p))/2;
    if(l <= mid) modify(p*2, l, r, v) //修改左子树区间
    if(r > mid)  modify(p*2+1, l, r, v) //修改右子树区间
    sum(p) = sum(p*2) + sum(p*2+1);
}
 
//区间查询
int query(int p, int l, int r){
    if(l <= l(p) && r >= r(p)) return sum(p); //完全覆盖
    spread(p); //如果区间未完全被覆盖,查询的时候也顺便传递标记信息
    int mid = (l(p) + r(p))/2;
    long long val = 0;
    if(l <= mid) val += query(p*2, l, r) //查询左子树区间,更新答案
    if(r > mid)  val += query(p*2+1, l, r) //修改右子树区间,更新答案
    return val;
}
 
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(1, 1, n);
    while(m--){
        char op[2];
        int l,r,v;
        scanf("%s%d%d", op, &l, &r);
        if(op[0] == 'C'){
            scanf("%d", &v);
            modify(1, l, r, v);
        }
        else printf("%lld\n", query(1, l, r));
        }
}
 
//分块算法
long long a[SIZE], sum[SIZE], tag[SIZE];
int L[SIZE], R[SIZE]; //每段左右端点
int pos[SIZE]; //每个位置属于哪一段
int n, m, t;
 
//区间修改
void modify(int l, int r, long long v){
    int p = pos[l], q = pos[r]; //查询区间所在的段编号
    if(p == q){ //同段内朴素求和
        for(int i = l; i <= r; i++) a[i] += v;
        sum[p] += v*(r - l + 1);
    }
    else {
        for(int i = p+1; i <= q-1; i++) tag[i] += v; //更新段的标记信息
        for(int i = l; i <= R[p]; i++) a[i] += v; //最左不足一段的区间
        sum[p] += v*(R[p] - l + 1); //更新段的前缀和
        for(int i = L[q]; i <= r; i++) a[i] += v; //最右不足一段的区间
        sum[q] += v*(r - L[p] + 1); //更新段的前缀和
    }
}
 
//区间查询
void query(int l, int r){
    int p = pos[l], q = pos[r];
    long long ans = 0;
    if(p == q){
        for(int i = l; i <= r; i++) ans += a[i];
        ans += tag[p] * (r - l + 1); //莫忘此段的标记信息
    }
    else {
        for(int i = p+1; i <= q-1; i++) //完整多段的求和
            ans += sum[i] + tag[i] * (R[i]-L[i]+1);
        for(int i = l; i <= R[p]; i++) ans += a[i]; //最左不足一段的区间
        ans += tag[p] * (R[p] - l + 1);
        for(int i = L[q]; i <= r; i++) ans += a[i]; //最右不足一段的区间
        ans += tag[p] * (r - L[p] + 1);
    }
    return ans;
}
 
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    //分块
    t = sqrt(n*1.0);
    for(int i = 1; i <= t; i++){ //各段的左右端点
        L[i] = (i-1) * sqrt(n) + 1;
        R[i] = i * sqrt(n);
    }
 
    if(R[t] < n){ //最末尾的一段
        t++;
        L[t] = R[t-1] + 1;
        R[t] = n;
    }
 
    //预处理 各个点属于哪一段,以及每段的前缀和
    for(int i = 1; i <= t; i++){
        for(int j = L[i]; j <= R[i]; j++){
            pos[j] = i;
            sum[i] += a[j];
        }
    }
    //指令
    while(m--){
        char op[3];
        int l,r,v;
        scanf("%s%d%d", op, &l, &r);
        if(op[0] == 'C'){
            scanf("%d", &v);
            modify(l, r, v);
        }
        else printf("%lld\n", query(l, r));
        }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
struct SegmentTree {
    int lc, rc; // 左右子节点编号
    int dat; // 区间最大值
} tree[MAX_MLOGN];
int tot, root[MAX_M]; // 可持久化线段树的总点数和每个根
int n, a;
 
int build(int l, int r) {
    int p = ++tot;
    if (l == r) {
        tree[p].dat = a[l];
        return p;
    }
    int mid = (l + r) >> 1;
    tree[p].lc = build(l, mid);
    tree[p].rc = build(mid + 1, r);
    tree[p].dat = max(tree[tree[p].lc].dat, tree[tree[p].rc].dat);
    return p;
}
// 在main函数中
root[0] = build(1, n);
 
//对于第i次修改,以可持久化线段树的第i-1个版本为基础,下面实现单点修改操作
 
int insert(int now, int l, int r, int x, int val) {
    int p = ++tot; //总点数+1
    tree[p] = tree[now]; //now为当前节点
    if (l == r) {
        tree[p].dat = val;
        return p;
    }
    int mid = (l + r) >> 1;
    if (x <= mid)
        tree[p].lc = insert(tree[now].lc, l, mid, x, val);
        //在这里实现logN个新节点的创建
    else
        tree[p].rc = insert(tree[now].lc, mid + 1, r, x, val);
        //同上
 
    tree[p].dat = max(tree[tree[p].lc].dat, tree[tree[p].rc].dat);
    //维护区间性质
    return p;
}
// 在main函数中
root[i] = insert(root[i - 1], 1, n, x, val);

  

posted @   我微笑不代表我快乐  阅读(89)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示