区间第K大问题,变化包括带修改和不带修改,强制在线和允许离线

修改主要是单点修改,我们前面也只讨论这种情况。

接下来我们从编程复杂度和时空复杂度来讨论几种做法。

1.整体二分(编程复杂度:低-中,时间复杂度:优秀,空间复杂度:优秀)

缺点:只能做离线

优点:空间都是O(n)。常数小。带修改O(nlog2n),不带修改O(nlogn)。

但是不带修改的情况,如果允许的话,个人认为加个树状数组写O(nlog2n)的更好写

这时单次solve中面对的问题是,数列中一些点是1其余都是0,然后求区间和的问题

这个问题把区间和变成前缀和相减,然后用vector来对那些需要求前缀的点做桶排

然后双指针即可做到单次solve为O(k),k为此次处理的操作数,总体O(nlogn)的复杂度

显然如果不差那log的时间,直接用树状数组来处理更好写

下面给了一个带单点修改查询区间第K的整体二分代码

 #include <bits/stdc++.h>

 #define lb(x) (x&(-x))

 using namespace std;

 const int N = 2e5 + ;

 int t, n, m, k, cnt;

 struct node {
int id, i, j, k;
}a[N], q1[N], q2[N]; int ans[N], b[N]; int c[N]; char op[]; void add(int i, int x) {while (i <= n) c[i] += x, i += lb(i);} int ask(int i) {int res = ; while (i > ) res += c[i], i -= lb(i); return res;} void solve(int head, int tail, int l, int r) {
if (head > tail) return;
if (l == r) {
for (int i = head; i <= tail; i ++)
ans[a[i].id] = r;
return;
}
int mid = l + r >> , s1 = , s2 = ;
for (int sum, i = head; i <= tail; i ++)
if (a[i].id) {
sum = ask(a[i].j) - ask(a[i].i - );
if (sum >= a[i].k) q1[s1 ++] = a[i];
else a[i].k -= sum, q2[s2 ++] = a[i];
}
else {
if (a[i].j <= mid) q1[s1 ++] = a[i], add(a[i].i, a[i].k);
else q2[s2 ++] = a[i];
}
for (int i = ; i < s1; i ++)
if (!q1[i].id)
add(q1[i].i, -q1[i].k);
memcpy(a + head, q1, sizeof(node) * s1);
memcpy(a + head + s1, q2, sizeof(node) * s2);
solve(head, head + s1 - , l, mid);
solve(head + s1, tail, mid + , r);
} int main() {
ios::sync_with_stdio(false);
for (cin >> t; t --; ) {
cin >> n >> m; k = cnt = ;
for (int i = ; i <= n; i ++) {
cin >> b[i];
a[++ k] = (node){, i, b[i], };
}
for (int l, r, x, i = ; i <= m; i ++) {
cin >> op >> l >> r;
if (op[] == 'Q') {
cin >> x;
a[++ k] = (node){++ cnt, l, r, x};
}
else {
a[++ k] = (node){, l, b[l], -};
a[++ k] = (node){, l, b[l] = r, };
}
}
solve(, k, , 1e9);
for (int i = ; i <= cnt; i ++)
printf("%d\n", ans[i]);
}
return ;
}

2.主席树(编程复杂度:低-中,时间复杂度:优秀,空间复杂度:高)

缺点:空间占用多。树套树的常数。

优点:可以在线!时间复杂度同整体二分。空间复杂度和时间复杂度一致。

不带修改是主席树基本操作。带修改就套树状数组,下面给出(和上面同一个问题的)代码。

 #include <bits/stdc++.h>

 using namespace std;

 const int MAXN = 1e9;
const int N = 5e4 + ; int n, m, a[N], rt[N]; int tot, tr[N * ][]; int tmp1[], tmp2[]; #define l(x) tr[x][0]
#define r(x) tr[x][1]
#define s(x) tr[x][2]
#define lb(x) (x&(-x))
#define mid (l + r >> 1) int change(int o, int l, int r, int k, int v) {
int x = ++ tot; s(x) = s(o) + v;
if (l == r) return x; l(x) = l(o), r(x) = r(o);
k > mid ? r(x) = change(r(o), mid + , r, k, v) : l(x) = change(l(o), l, mid, k, v);
return x;
} void modify(int i, int p, int v) {
while (i <= n) rt[i] = change(rt[i], , MAXN, p, v), i += lb(i);
} int ask(int l, int r, int k) {
if (l == r) return r; int sum = ;
for (int i = ; i <= tmp1[]; i ++) sum -= s(l(tmp1[i]));
for (int i = ; i <= tmp2[]; i ++) sum += s(l(tmp2[i]));
if (k > sum) {
for (int i = ; i <= tmp1[]; i ++) tmp1[i] = r(tmp1[i]);
for (int i = ; i <= tmp2[]; i ++) tmp2[i] = r(tmp2[i]);
return ask(mid + , r, k - sum);
}
else {
for (int i = ; i <= tmp1[]; i ++) tmp1[i] = l(tmp1[i]);
for (int i = ; i <= tmp2[]; i ++) tmp2[i] = l(tmp2[i]);
return ask(l, mid, k);
}
} int query(int l, int r, int k) {//查询区间第k小
tmp1[] = tmp2[] = ;
for (int i = l - ; i > ; i -= lb(i)) tmp1[++ tmp1[]] = rt[i];
for (int i = r; i > ; i -= lb(i)) tmp2[++ tmp2[]] = rt[i];
return ask(, MAXN, k);
} int main(){
int t; char op[];
for (cin >> t; t --; ) {
cin >> n >> m; tot = ;
for (int i = ; i <= n; i ++) cin >> a[i], rt[i] = ;
for (int i = ; i <= n; i ++) modify(i, a[i], );
for (int i, j, k; m --; ) {
cin >> op >> i >> j;
if (op[] == 'C') modify(i, a[i], -), modify(i, a[i] = j, );
else cin >> k, printf("%d\n", query(i, j, k));
}
}
return ;
}

其他做法我参考了一下,很难与上述两种做法并肩,就不做讨论了

如果有的话欢迎告诉我

拓展1.初始数列每个位置都是一个空队列,修改变成了区间[l,r]的每个队列末尾都push一个数x

          查询某个区间所有数字中的第K大

解法:其实还是个整体二分的简单题目,用线段树维护即可,复杂度依然是O(nlog2n)

区间第k大的几种解法的更多相关文章

  1. 静态区间第k大 树套树解法

    然而过不去你谷的模板 思路: 值域线段树\([l,r]\)代表一棵值域在\([l,r]\)范围内的点构成的一颗平衡树 平衡树的\(BST\)权值为点在序列中的位置 查询区间第\(k\)大值时 左区间在 ...

  2. 【POJ】【2104】区间第K大

    可持久化线段树 可持久化线段树是一种神奇的数据结构,它跟我们原来常用的线段树不同,它每次更新是不更改原来数据的,而是新开节点,维护它的历史版本,实现“可持久化”.(当然视情况也会有需要修改的时候) 可 ...

  3. HDU3473--Minimum Sum(静态区间第k大)

    Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...

  4. 解决区间第K大的问题的各种方法

    例题:http://poj.org/problem?id=2104 最近可能是念念不忘,必有回响吧,总是看到区间第k大的问题,第一次看到是在知乎上有人面试被弄懵了后来又多次在比赛中看到.以前大概是知道 ...

  5. Dynamic Rankings——带修改区间第k大

    三种做法:1.整体二分: 二分mid 考虑小于mid的修改的影响 但是大于mid的修改可能会干掉小于mid的一些值 所以额外把一个修改变成一个值的删除和一个值的添加 这样就相互独立了! 整体二分,树状 ...

  6. Permutation UVA - 11525(值域树状数组,树状数组区间第k大(离线),log方,log)(值域线段树第k大)

    Permutation UVA - 11525 看康托展开 题目给出的式子(n=s[1]*(k-1)!+s[2]*(k-2)!+...+s[k]*0!)非常像逆康托展开(将n个数的所有排列按字典序排序 ...

  7. POJ 2104 静态找区间第k大

    静态区间第k大的问题,往往可以利用主席树来解决 这是主席树的第一道题 主席树大概可以理解为在n个节点上都建立一棵线段树,但是想想会超出内存 每一个节点保存的线段树都记录当前整段前缀区间的信息 但是因为 ...

  8. 【ZOJ2112】【整体二分+树状数组】带修改区间第k大

    The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with t ...

  9. 【POJ2761】【区间第k大】Feed the dogs(吐槽)

    Description Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed the dogs ...

随机推荐

  1. 2,Spring MVC 学习总结(二)- 方法(Action)参数映射

    一,Controller层方法(Action)参数映射 1,自动参数映射 1.1,基本数据类型参数映射 方法的参数可以是任意基本数据类型,如果方法参数名与http中请求的参数名称相同时会进行自动映射. ...

  2. 如何在浏览器上安装 VueDevtools工具

    火狐浏览器直接打开附加组件中,搜索 VueDevtools,找到安装即可. 谷歌浏览器--更多工具--扩展程序--打开下载好的VueDevtools整体拖进去就行了

  3. Delphi Base64编码/解码

    Uses CnBase64: CnBase64.Base64Encode(Edit1.Text, Psw64);

  4. CentOS 如何将.deb 文件 转换.rpm, centos安装deb包

    CentOS 如何将.deb 文件 转换.rpm [root@localhost tmp]#tar zxvf alien_8.88.tar.gz yum install alien [root@loc ...

  5. Red Gate .NET Reflector

    Debug and decompile inside Visual Studio (VSPro edition) Use the Visual Studio debugger Use your reg ...

  6. selenium,webdriver爬取斗鱼主播信息 实操

    from selenium import webdriver import time from bs4 import BeautifulSoup class douyuSelenium(): #初始化 ...

  7. 一步一步学Vue(九) 路由元数据

    一步一步学Vue(九):https://www.cnblogs.com/Johnzhang/p/7260888.html

  8. 44-python基础-python3-字符串-常用字符串方法(二)-isalpha()-isalnum()-isdigit()-isspace()-istitle()

    3-isX 字符串方法   序号 方法 条件 返回结果1 返回结果2 1 isalpha() 如果字符串只包含字母,并且非空; True False 2 isalnum() 如果字符串只包含字母和数字 ...

  9. 分布式ID的雪花算法及坑

    分布式ID生成是目前系统的常见刚需,其中以Twitter的雪花算法(Snowflake)比较知名,有Java等各种语言的版本及各种改进版本,能生成满足分布式ID,返回ID为Long长整数 但是这里有一 ...

  10. cookie,seesion学习

    一,为什么需要cookie和session? 1,Web应用程序是使用HTTP协议传输数据的.然而HTTP协议是无状态的协议.一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的 ...