区间第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. QString的arg方法

    第一个参数是要填充的数字,第二个参数为最小宽度,第三个参数为进制,第四个参数为当原始数字长度不足最小宽度时用于填充的字符,如 QString name=QString("R%1C%2&quo ...

  2. socket 接收和发送缓冲区

    问题产生: 在进行客户端向服务端发送数据时,每次发送一定数量数据后发送端就等不到send函数的返回,导致程序一直卡死在send函数. 通过抓包发现:发送端发送过快而接收端处理速度过慢,导致快速发送一定 ...

  3. 通过TCP/IP连接Mysql数据库

    问题:mysql只能用localhost或127.0.0.1连接 解决:mysql安装完后,默认是root用户,root用户只能在服务器登录,需要分配新用户. 1.以root用户登陆mysql数据库. ...

  4. 在使用bat 批处理 时将运行结果显示并保存到文件中 echo

    实现原理: 因为要输出到文本,所以可以使用call将结果输出到临时文件,完成之后做3件事: 1. 将临时文本内容显示,实现窗口显示的本次运行结果的功能,可先清屏. 2. 将临时文本内容追加到日志文件用 ...

  5. 怎样使用haskell编写应用程序

    参考:http://stackoverflow.com/a/9153617 http://www.haskell.org/haskellwiki/How_to_write_a_Haskell_prog ...

  6. 记录规则(recording rules)与告警规则(alerting rule)

    记录规则(recording rules) 配置规则 Prometheus支持两种类型的规则,可以对其进行配置,然后定期进行评估:记录规则和警报规则.要在Prometheus中包含规则,请创建一个包含 ...

  7. 中介者模式(Mediator Pattern)

    用于减少多个对象或类之间的通信复杂性. 此模式提供了一个中介类,它通常处理不同类之间的所有通信,并支持通过松散耦合来维护代码.中介者模式属于行为模式类别. 实现实例 在这里通过一个聊天室的示例来演示中 ...

  8. Linux中zip压缩和解压缩命令

    主要参数 -c:将解压缩的结果-l:显示压缩文件内所包含的文件-p:与-c参数类似,会将解压缩的结果显示到屏幕上,但不会执行任何的转换-t:检查压缩文件是否正确-u:与-f参数类似,但是除了更新现有的 ...

  9. QT简介及下载

    |   版权声明:本文为博主原创文章,未经博主允许不得转载. Qt是一个1991年由奇趣科技开发的跨平台C++图形用户界面应用程序开发框架.它既可以开发GUI程序,也可用于开发非GUI 程序,比如控制 ...

  10. ORCAL 数据库的约束以及SQL语言的四种类型

    oracle数据库约束: 定义:要输入的这个值是一个什么样的值, 或者是哪个范围内的值 作用: 确保完整性, 确保精确性 1, 非空约束(not null) 记录一条信息的时候如果用户名和密码没有被记 ...