平衡树

#


# 洛谷P1486_郁闷的出纳员

# 🔗

20220914151347

# 💡

新来员工、离开员工
这就是一个加点和删点的问题
然后看见数值加、数值减、查询全局第 kk 大,秒用平衡树
全局的数值加减用一个懒标记实时 pushdown 即可
小的要退出,就每次操作结束后找到最左边的数,判断一下是不是小于下界,小的话删掉
查询全局第 kk 大就用记录的子树大小 size 在树上走就行了

#

const int N = 3e5 + 10;
struct node {
    int l, r;
    int key, val;
    int lazy, size;
} t[N];
int cnt, root;
mt19937 rnd(233);

inline int newnode (int val) {
    t[++cnt] = {0, 0, (int)rnd(), val, 0, 1};
    return cnt;
}
inline void pushup (int now) {
    t[now].size = t[t[now].l].size + t[t[now].r].size + 1;
}
inline void pushdown (int now) {
    if (!t[now].lazy) return;
    t[now].val += t[now].lazy;
    if (t[now].l) t[t[now].l].lazy += t[now].lazy;
    if (t[now].r) t[t[now].r].lazy += t[now].lazy;
    t[now].lazy = 0;
}
inline void Split (int now, int val, int &x, int &y) {
    if (!now) {
        x = y = 0;
    } else {
        pushdown(now);
        if (val < t[now].val) {
            y = now;
            Split(t[now].l, val, x, t[now].l);
        } else {
            x = now;
            Split(t[now].r, val, t[now].r, y);
        }
        pushup(now);
    }
}
inline int Merge (int x, int y) {
    if (!x || !y) return x + y;
    if (t[x].key >= t[y].key) {
        pushdown(x);
        t[x].r = Merge(t[x].r, y);
        pushup(x);
        return x;
    } else {
        pushdown(y);
        t[y].l = Merge(x, t[y].l);
        pushup(y);
        return y;
    }
}
inline void Insert (int val) {
    int x, y;
    Split(root, val, x, y);
    root = Merge(Merge(x, newnode(val)), y);
}
inline void Delete (int val) {
    int x, y, z; 
    Split(root, val, x, z);
    Split(x, val - 1, x, y);
    y = Merge(t[y].l, t[y].r);
    root = Merge(Merge(x, y), z);
}
inline int Min (int now) {
    pushdown(now);
    while (t[now].l) now = t[now].l, pushdown(now);
    return t[now].val;
}
inline int Num (int rnk) {
    int now = root;
    while (now) {
        pushdown(now);
        if (rnk == t[t[now].l].size + 1) break;
        if (rnk <= t[t[now].l].size) now = t[now].l;
        else rnk -= t[t[now].l].size + 1, now = t[now].r;
    }
    return t[now].val;
}

int SIZE, LEAVE;
int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, down; cin >> n >> down;

    while (n --) {
        char op; cin >> op;
        int k; cin >> k;
        switch (op) {
            case 'I':
                if (k >= down) 
                    Insert(k), 
                    SIZE ++;
                break;
            case 'A':
                t[root].lazy += k;
                break;
            case 'S':
                t[root].lazy -= k;
                break;
            default:
                if (k > SIZE) cout << "-1\n";
                else cout << Num(SIZE - k + 1) << endl;
                break;
        }

        while (SIZE > 0) {
            int mn = Min(root);
            if (mn < down) Delete(mn), SIZE --, LEAVE ++;
            else break;
        }
    }
    cout << LEAVE << 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
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

# CodeForces1042D_PetyaAndArray

# 🔗

20220707190615

# 💡

(设前缀和为 [pre][pre]
问题就是累加 对于每一个 ii ,它前面的 jj 有多少个满足 preiprej<kpre_i-pre_j<k
本来想二分或者双指针直接莽,突然发现它的前缀和不存在单调性,没法移动
式子可以变成 prej+k>preipre_j+k>pre_i 那这个问题就只能用数据结构边计算边更新,每次累加查询数据结构里面插入过的大于 preipre_i 的数量,然后塞入 pre+kpre+k
用权值线段树了话发现这些数都特别大,并且查询范围时存在加减关系,离散化有一点麻烦
而只有大小关系,可以直接平衡树实现:将 fhqtreapfhq-treapvalval 分裂后右树的大小就是查询结果

#

const int N = 2e5 + 10;
struct node {
        int l, r;
        ll val; int key;
        int size;
} t[N];
int root, cnt;
mt19937 rnd(233);
inline int newnode (ll val) {
        t[++cnt] = {0, 0, val, (int)rnd(), 1};
        return cnt;
}
inline void pushup (int now) {
        t[now].size = t[t[now].l].size + t[t[now].r].size + 1;
}
inline void Split (int now, ll val, int &x, int &y) {
        if (!now) {
                x = y = 0;
        } else {
                if (val < t[now].val) {
                        y = now;
                        Split(t[now].l, val, x, t[y].l);
                        pushup(y);
                } else {
                        x = now;
                        Split(t[now].r, val, t[now].r, y);
                        pushup(x);
                }
        }
}
inline int Merge (int x, int y) {
        if (!x || !y) return x + y;
        if (t[x].key >= t[y].key) {
                t[x].r = Merge(t[x].r, y);
                pushup(x);
                return x;
        } else {
                t[y].l = Merge(x, t[y].l);
                pushup(y);
                return y;
        }
}
inline void Insert (ll val) {
        int x, y;
        Split(root, val, x, y);
        root = Merge(Merge(x, newnode(val)), y);
}
inline int get_BiggerSize (ll val) {
        int x, y;
        Split(root, val, x, y);
        int res = t[y].size;
        root = Merge(x, y);
        return res;
}

int n; 
ll k;
int main () {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);

        cin >> n >> k;
        ll sum = 0;
        ll res = 0;    
        Insert(k);
        for (int i = 0; i < n; i ++) {
                ll x; cin >> x;
                sum = sum + x;
                res = res + get_BiggerSize(sum);
                Insert(sum + k);
        } 
        cout << res << 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
65
66
67
68
69
70
71
72
73

Last Updated: 10/14/2023, 7:51:49 PM