Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 数论

  • 动态规划

  • 暴力

  • 图论

  • 数据结构

    • Link Cut Tree学习笔记
    • Splay学习笔记
    • 树状数组学习笔记
      • 例题洛谷P3865
      • 例题Codeforces 12D
      • 例题 POJ 2985
    • 树链剖分学习笔记
    • 线性基学习笔记
    • 线段树学习笔记
    • 舞蹈链学习笔记
    • 数据结构-树
  • 字符串

  • 几何

  • 博弈论

  • 其他

树状数组学习笔记

# 树状数组求前缀和

int val[maxn];
void modify(int pos, int delta){
    for(int i=pos; i<=max_pos; i+=(-i)&i) val[i] += delta;
}
int query(int pos){
    int ret = 0;
    for(int i=pos; pos>0; i-=(-i)&i) ret += val[i];
    return ret;
}
1
2
3
4
5
6
7
8
9

# 树状数组求区间最值

# 例题洛谷P3865 (opens new window)

ST表的模板题,肯定不能用ST做啦~

注:从代码中可以看出,该方法对在线修改类型有限制(数值只能增加不能减少),但是离线完全没问题

ST表(1.22s,8.51M),树状数组(1.29s,6.9M),耗时差不多,但内存占用少了

#include <bits/stdc++.h>
#define Android ios::sync_with_stdio(false), cin.tie(NULL)

using namespace std;

const int maxn = 1e6 + 100;

int bit[maxn] = {0}, val[maxn];

void update(int x, int pos, int n){
    while(pos <= n){
        bit[pos] = max(bit[pos], x);
        pos += (-pos) & pos;
    }
}

int query(int l, int r){ // 结果包含左/右端点
    int ans = val[r], tmp;
    while(l <= r){ // 如果l可能为0,需要特判
        tmp = r - (r & (-r));
        if(tmp >= l){
            ans = max(ans, bit[r]);
            r = tmp;
        }else{
            ans = max(ans, val[r--]); // 当减去lowbit后小于l时,同原数组对比
        }
    }
    return ans;
}

signed main(){
    int n, m;
    cin >> n >> m;
    for(int i=1; i<=n; i++) {
        cin >> val[i];
        update(val[i], i, n);
    }
    for(int i=0, l, r; i<m; i++){
        cin >> l >> r;
        cout << query(l, r) << '\n';
    }
}
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

# 树状数组处理二位偏序

# 例题Codeforces 12D

题意:求出满足∃i,ax<ai&bx<bi&cx<ci\exist i,\ a_x<a_i \&\ b_x<b_i\&\ c_x<c_i∃i,ax​<ai​&bx​<bi​&cx​<ci​的xxx的个数

#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 5e5 + 1000;

struct val {
    int a, b, c, idx;
} vals[maxn], tmp[maxn];

bool cmp1(const val & v1, const val & v2){
    if(v1.a == v2.a) {
        if(v1.b == v2.b) return v1.c < v2.c;
        else return v1.b < v2.b;
    }
    return v1.a < v2.a;
}

int va[maxn]={0};
int greatest;
// 树状数组维护p到greatest中最大的值
int query(int p) {
    int ret = 0;
    for(int i = p; i <= greatest; i += (-i)&i) ret = max(ret, va[i]);
    return ret;
}

void add(int p, int v) {
    for(int i = p; i > 0; i -= (-i)&i) va[i] = max(va[i], v);
}

signed main() {
    int n, ans=0;
    cin >> n;
    vector<int> unq(n + 1);
    for(int i = 1; i <= n; i++) cin >> vals[i].a, vals[i].idx = i;
    for(int i = 1; i <= n; i++) {
        cin >> vals[i].b;
        vals[i].b++;
        unq[i] = vals[i].b;
    }
    for(int i = 1; i <= n; i++) cin >> vals[i].c;
    
    // 对b离散化
    sort(unq.begin(), unq.end());
    unq.resize(unique(unq.begin(), unq.end()) - unq.begin());
    greatest = unq.size() + 3;
    for(int i=1; i<=n; i++) vals[i].b = lower_bound(unq.begin(), unq.end(), vals[i].b) - unq.begin();

    sort(vals + 1, vals + n + 1, cmp1); // 对a排序,问题降为二位偏序问题

    int j = n;
    for(int i=n; i>0; i--){
        if(vals[i].a < vals[j].a){
            for(; j>i; j--) add(vals[j].b, vals[j].c);
        }
        if(query(vals[i].b+1) > vals[i].c) {
            ans++;
        }
    }
    cout << ans << endl;
}
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

# 树状数组求第K小

这里我们不使用二分+求前缀和的方法(复杂度O(log2n)O(log^2n)O(log2n))

举个例子:假设151515为第444小的数字,可以拆分成8+4+2+18+4+2+18+4+2+1,因此,在查询151515是否为第kkk大时,我们可以先查询888,结果发现cnt[8]<4cnt[8]< 4cnt[8]<4,接着向查询得cnt[8+4]<4,cnt[8+4+2]<4,cnt[8+4+2+1]≥4cnt[8+4]<4,cnt[8+4+2]<4,cnt[8+4+2+1]\ge 4cnt[8+4]<4,cnt[8+4+2]<4,cnt[8+4+2+1]≥4,因此,满足cnt[x]<4cnt[x]<4cnt[x]<4的最大位置为x=14x=14x=14,第444的数字为14+114+114+1

# 例题 POJ 2985

题意:有nnn只猫,初始第iii只猫属于第iii组。对于操作1 u v,要将组u,vu,vu,v合并。对于操作2 k,查询第kkk大的组的大小。

注意:在poj上交题,务必使用scanf而非cin

AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 2e5 + 1000;
int union_set[maxn], cat[maxn];

int lookup(int u){return u==union_set[u]?u:union_set[u]=lookup(union_set[u]);}

int fenwick[maxn], n, m;

void add(int pos, int delta){
    for(int i=pos; i<=n; i+=(-i)&i){
        fenwick[i] += delta;
    }
}

int query(int k){ // 查询第k小
    int pos = 0, cnt = 0;
    for(int i=20; i>=0; i--){
        pos += (1<<i);
        if(pos > n || cnt + fenwick[pos] >= k){
            pos -= (1<<i);
        }else{
            cnt += fenwick[pos];
        }
    }
    return pos + 1;
}

void solve(){
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) {
        union_set[i] = i;
        cat[i] = 1;
    }
    add(1, n);

    int group_cnt = n;
    for(int i=0, op, a, b; i<m; i++){
        scanf("%d", &op);
        if(op == 0){
            scanf("%d%d", &a, &b);
            a = lookup(a), b = lookup(b);
            if(a != b){
                group_cnt--;
                add(cat[a], -1);
                add(cat[b], -1);
                add(cat[a] = cat[a] + cat[b], 1);
                union_set[b] = a;
            }
        }else{
            scanf("%d", &a);
            // 转化为查询第 group_cnt - a + 1 小
            cout << query(group_cnt - a + 1) << '\n';
        }
    }
}

signed main(){
    solve();
}
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

# 参考

https://www.cnblogs.com/wuyiqi/archive/2011/12/25/2301071.html

上次更新: 2021/02/24, 03:37:30
Splay学习笔记
树链剖分学习笔记

← Splay学习笔记 树链剖分学习笔记→

最近更新
01
深入理解Java虚拟机
03-04
02
DNS工作原理
02-27
03
改用VuePress啦
02-23
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式