「Luogu P2617」Dynamic Rankings

给出一段序列,每次修改某个数的值和询问区间第 k 小。\((1\le n,m\le 10^5,0\le a_i\le10^9)\)

Luogu

分析

动态主席树裸题。

树状数组套主席树,树状数组的每个结点相当于一棵主席树,每次修改操作只在对应树状数组的 logn 个结点所对应的主席树上修改,查询时,将 l - 1 和 r 分别对应的 logn 棵主席树作差即可。

时间复杂度: \(O(n\log^2{n})\)

空间复杂度: \(O(n\log{n})\)

代码

#include <bits/stdc++.h>

#define N 100005
#define lowbit(i) i&-i
#define REP(i, l, r) for (int i = (l); i != (r); ++i)
#define FOR(i, l, r) for (int i = (l); i <= (r); ++i)
#define DRP(i, l, r) for (int i = (l); i != (r); --i)
#define DFR(i, l, r) for (int i = (l); i >= (r); --i)

using namespace std;

int gi() {
    int x = 0, f = 1; char c = getchar();
    for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

struct updates { int a, b, k, d; } q[N];

int n, m, tot, len, sx, sy;
int s[N], hs[N << 1], rx[N], ry[N];
int rt[N], cnt[N << 10], L[N << 10], R[N << 10];

void insert(int lst, int &now, int l, int r, int k, int v) {
    if (!now) now = ++tot;
    cnt[now] = cnt[lst] + v;
    if (l == r) return;
    int mid = l + r >> 1;
    if (k <= mid) R[now] = R[lst], insert(L[lst], L[now], l, mid, k, v);
    else L[now] = L[lst], insert(R[lst], R[now], mid + 1, r, k, v);
}

void modify(int i, int v) {
    int k = lower_bound(hs + 1, hs + 1 + len, s[i]) - hs;
    while (i <= n) {
        insert(rt[i], rt[i], 1, len, k, v);
        i += lowbit(i);
    }
}

void get(int x, int y) {
    sx = sy = 0;
    while (x) rx[++sx] = rt[x], x -= lowbit(x);
    while (y) ry[++sy] = rt[y], y -= lowbit(y);
}

int query(int l, int r, int k) {
    if (l == r) return l;
    int sum = 0;
    FOR(i, 1, sx) sum -= cnt[L[rx[i]]];
    FOR(i, 1, sy) sum += cnt[L[ry[i]]];
    int mid = l + r >> 1;
    if (k <= sum) {
        FOR(i, 1, sx) rx[i] = L[rx[i]];
        FOR(i, 1, sy) ry[i] = L[ry[i]];
        return query(l, mid, k);
    }
    else {
        FOR(i, 1, sx) rx[i] = R[rx[i]];
        FOR(i, 1, sy) ry[i] = R[ry[i]];
        return query(mid + 1, r, k - sum);
    }
}

int main() {
    char ch;
    len = n = gi(), m = gi();
    FOR(i, 1, n) s[i] = hs[i] = gi();
    FOR(i, 1, m) {
        cin >> ch;
        if (ch == 'Q') q[i] = (updates){gi(), gi(), gi(), 1};
        else q[i] = (updates){gi(), hs[++len] = gi(), 0, 0};
    }
    sort(hs + 1, hs + 1 + len);
    len = unique(hs + 1, hs + 1 + len) - hs - 1;
    FOR(i, 1, n) modify(i, 1);
    FOR(i, 1, m) {
        if (q[i].d) {
            get(q[i].a - 1, q[i].b);
            printf("%d\n", hs[query(1, len, q[i].k)]);
        }
        else {
            modify(q[i].a, -1);
            s[q[i].a] = q[i].b;
            modify(q[i].a, 1);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hlw1/p/12257988.html

时间: 2024-10-07 19:15:04

「Luogu P2617」Dynamic Rankings的相关文章

「luogu2617」Dynamic Rankings

「luogu2617」Dynamic Rankings 传送门 树套树直接上树状数组套主席树,常数很大就是了. 树套树参考代码: /*-------------------------------- Code name: DynamicRanking.cpp Author: The Ace Bee This code is made by The Ace Bee --------------------------------*/ #include <cstdio> #include <

「Luogu 1821」[USACO07FEB]银牛派对Silver Cow Party

更好的阅读体验 Portal Portal1: Luogu Portal2: POJ Description One cow from each of N farms \((1 \le N \le 1000)\) conveniently numbered \(1 \cdots N\) is going to attend the big cow party to be held at farm #X \((1 \le X \le N)\). A total of \(M (1 \le M \l

「Luogu 2367」语文成绩

更好的阅读体验 Portal Portal1: Luogu Description 语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行.她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少.你能帮帮她吗? Input 第一行有两个整数\(n\),\(p\),代表学生数与增加分数的次数. 第二行有\(n\)个数,\(a_1 \sim a_n\),代表各个学生的初始成绩. 接下来\(p\)行,每行有三个数,\(x\),\(y\),\(z\),代表给第\(x\)个到第\(y\)个学生每人增

「Luogu P3302」[SDOI2013]森林

给出一片森林,每个点有一个权值,要求支持动态连边,并回答任意两点间第 k 小权值,强制在线.\((1\le N,M,T \le 8\times 10^4)\) Luogu 分析 求第 k 小权值,这个肯定是用主席树了,但连边该怎么办?LCT?可我不会. 我们可以用启发式合并的方法,连边也就是合并两棵树,我们每次将较小的树连到较大的树上去,更新信息就暴力 dfs 较小树中的每一个点就好了. 代码 #include <bits/stdc++.h> #define N 80003 #define D

「Luogu P3178」[HAOI2015]树上操作

有一棵点数为 \(N\) 的树,以点 \(1\) 为根,且树点有边权.然后有 \(M\) 个操作,分为三种: 操作 1 :把某个节点 \(x\) 的点权增加 \(a\) . 操作 2 :把某个节点 \(x\) 为根的子树中所有点的点权都增加 \(a\) . 操作 3 :询问某个节点 \(x\) 到根的路径中所有点的点权和. Luogu 分析 我们把树上问题利用 \(dfs\) 序转化成序列问题然后直接上线段树解决即可. 考虑将线段树的每个叶子结点设为在原树上的点到根的点权和.对于单点修改,当前结

「Luogu P6101」[EER2]出言不逊

Portal Portal1: Luogu Solution 模拟,先找到在读入字符串内出现次数最多的字符,记录个数,然后以 \(2\) 为指数在现有长度上递增,就可以算出答案. 但是long long会溢出,所以要判断一下,如mx + mx < mx说明已经溢出了,然后就退出答案做个标记,输出的时候\(+1\),否则会死循环,至于__int128,我没试过. 代码纯属是为过而过,没什么可看的. Code #include<bits/stdc++.h> #pragma GCC optim

「 Luogu P2285 」打鼹鼠

解题思路 第一眼看上去觉得要设计一个三维的 DP,$dp[i][j][k]$ 表示在 $(i,j)$ 这个位置上 $k$ 时刻能够打死的最多的鼹鼠. 但是被数据范围卡死.完全开不开数组啊. 然后注意到题目中有句话说保证是按照时间递增序输入的. 想一下,某个鼹鼠能够被打死,是因为他之前被打死的鼹鼠走过来的. 所以只需要考虑每个鼹鼠之前的若干鼹鼠就可.只要两个鼹鼠的距离小于它们出现的时刻只差,那就可以扩展的到. 附上代码 #include <iostream> #include <cstri

「 COGS 2240 」 X 「 Luogu P2885 」 架设电话线

解题思路 首先很容易就想到了一个二维的朴素的 $dp$. 设 $dp[i][j]$ 表示第 $i$ 个位置的电话线杆的高度为 $j$ 时的最小花费,就需要枚举第 $i$ 个电话线杆.第 $i$ 个电话线杆的高度 $j$.第 $i-1$ 个电话线杆的高度 $k$. 状态转移方程如下 $$dp[i][j] = \min \{dp[i-1][k]+|j-k|\times c + (j-h[i])^2\}$$ 但是这样的 $dp$ 过不了这题的数据范围.这个 $dp$ 的时间复杂度是 $\text{O}

「 Luogu P2657 」 windy数

# 题目大意 给出区间 $[a,b]$,求出区间中有多少数满足下列两个条件 不含有前导 $0$. 相邻两个数字之差的绝对值至少是 $2$. # 解题思路 数位 $DP$,用记忆化搜索来实现.设 $dp[i][j]$ 表示现在已经枚举到第 $i$ 位,第 $i+1$ 位是 $j$ 时一共有多少满足条件的数. 还是直接看代码里的注释吧. # 放上代码 #include <algorithm> #include <iostream> #include <cstring> #i