传送门
动态区间第KKK大问题,单点修改(这里是第kkk小,即是从小到大第kkk个)

这里还有个区间修改,有点类似的 P3332 [ZJOI2013]K大数查询

分析

树套树

树套树,就是用一种树形结构维护另一个数据结构
形式多样,根据需求定制

这里是,动态区间第KKK大
考虑静态的情况下,解法有
在线:主席树,划分树
离线:整体二分

如果要支持修改,划分树是根据最终结果排序后再构造出来的树,所以不资瓷修改
剩下的,主席树和整体二分了,整体二分下面再说

主席树静态区间,每一个版本的值域情况
对每一个位置构造一个权值线段树,但是使用了前面的信息,也就是在更新的时候,进行了克隆原来版本的信息,克隆之后在版本中更新,大大减少了空间使用
假如我修改了iii位置的信息,由于存在依赖关系(上面的关系就是),就需要修改i+1i+1i+1位置的权值线段树信息,一直修改到最后
这样每次修改最坏的复杂度就是 nlognnlognnlogn,明显不优
我们深层次考虑静态区间第KKK大的本质
我们对于每一个位置建立一个权值线段树,这个位置上的权值线段树,就是这个位置前面的数,数量的一个前缀和
修改时要修改后面的信息,目的就是维护这个前缀和
那么我们有什么数据结构能够解决这个前缀和问题呢?
树状数组或者是线段树

  • 树状数组,每次修改需要修改lognlognlogn个节点,总共有nnn个节点
  • 线段树,每次修改需要修改lognlognlogn个节点,总共有2∗n−12*n-12∗n−1个节点

这里权衡时间和空间的复杂度,使用树状数组进行外层树形结构,维护内层的权值线段树(事实上,树状数组也写起来更简单,这种动态开点线段树最怕的就是空间问题,小了不行,大了MLEMLEMLE)

那么,树状数组维护的过程中,需要修改lognlognlogn个节点,我们只在这几个节点上进行修改,最后我们查询查这lognlognlogn个节点的信息才是对的。
这里注意,每次询问前我们都要预先处理好这lognlognlogn个节点,便于我们在询问的时候求前缀和,获取版本信息

修改问题解决了,查询就只需要在树上进行二分查找即可
先求出当前版本的左边一半的数有多少个(对右边lognlognlogn个节点求值,减去左边lognlognlogn个节点的值),记为xxx

  • 如果小于kkk则往右边找,k−=xk-=xk−=x,往右边找第k−xk-xk−x大的
  • 如果大于kkk则往左边找,说明答案一定是在左边第kkk大

整体二分

先考虑静态的,对每一个查询都保存下来,记为查询询问
把每一个下标对应的数和下标也保存下来,记为插入询问
可以预先离散化一下出现的值
对第kkk大进行二分

假设当前二分答案为midmidmid
把所有插入询问值小于midmidmid(或者是midmidmid代表的值)的位置上加111,且分到左区间,其它的分到右区间

对所有的查询询问,查询当前答案下,l∼rl \sim rl∼r 区间有多少个打上了标记111,也就是查区间和,记为cntcntcnt

  • 如果 cntcntcnt 小于等于 kkk,说明答案在大的区间,把这个询问放在右区间,因为左边已经有了cntcntcnt个,所以答案在右区间的第k−cntk-cntk−cnt小,kkk要减去cntcntcnt
  • 如果 cntcntcnt 大于 kkk,说明答案在小的区间,把这个询问放在左区间,kkk不用变,因为答案仍然是左区间第kkk小

二分的过程中,在取等于条件的那里临时记录答案
即是在,小于等于那里记录
这里笔者采用的是如下的二分形式,注意一下和其它的二分格式
这里是二分过程中记录答案,并且更新答案(这样好理解些)

while (l <= r) {mid = l + ((r-l)>>1);if (check(mid)) {ans = mid;l = mid + 1;} else {r = mid - 1;}
}

对于上面插入询问分左右问题,这里做下解释:
左区间为答案小的区间,右区间为答案大的区间
如果一个插入的值大于当前的midmidmid的话,不会给小区间的答案造成影响(小区间不会使用到这个插入询问,这个位置上一定不会被置为111),只有右边区间才会使用到,所以放在右边小于的同理

接下来,考虑动态的
由于动态多了一个操作,会单点修改某一个值
这里我们同样可以把修改操作加入到插入询问
前面的常规的插入操作是,在某一个位置加 111
这里修改操作可以分解为

  • 在某一个位置−1-1−1(删去原先的值影响)
  • 在这个位置上+1+1+1(修改为当前的值)

为什么能够保证这里的正确性呢?
因为初始的插入询问一定在所有询问前,完成初始化的效果
之后的修改操作,以及查询操作,都是按时间顺序进行二分的

这里大概证明一下:
如果当前二分的答案是midmidmid,某个位置修改前为aaa,修改后为bbb

  • 如果aaa和bbb都大于midmidmid,都不执行,对此时二分的答案的判定无影响,被分配到右区间
  • 如果aaa和bbb都小于等于midmidmid,此时两个操作都会执行,判定影响为,“−1-1−1然后+1+1+1”也就是不变,两个都被分配到左区间
  • 如果aaa小于等于midmidmid,bbb大于midmidmid,此时aaa执行,bbb不执行,对判定影响为“−1-1−1”,aaa分配到左区间,bbb分在右区间
  • 如果aaa大于midmidmid,bbb小于等于midmidmid,此时aaa不执行,bbb执行,对判定影响为“+1+1+1”

综上,这个修改操作无论在什么情况都只会执行影响判定的那一部分,且和真实修改情况一致,正确性显然


代码

树套树

//P3380
/*@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;int arr[MAX_N];
int uniarr[MAX_N];
int unicnt = 0;struct Qr {int opt, l, r, k;
}qr[MAX_N];struct Tr {int k, l, r;
}tr[MAX_N<<4];
int indx = 0;
int root[MAX_N];void push_up(int rt) {tr[rt].k = tr[tr[rt].l].k + tr[tr[rt].r].k;
}void update(int& rt, int l, int r, int x, int k) {if (!rt) rt = ++indx;if (l == r) {tr[rt].k += k;return;}int mid = l + ((r-l)>>1);if (x <= mid) update(tr[rt].l, l, mid, x, k);if (x  > mid) update(tr[rt].r, mid+1, r, x, k);push_up(rt);
}int cnt[2];
int son[2][MAX_N];
int ans[2][MAX_N];int query(int l, int r, int k) {if (l == r) {return l;}int mid = l + ((r-l)>>1);int sum = 0;int lsum = 0;for (int i = 1; i <= cnt[1]; ++i) {sum += tr[son[1][i]].k;lsum += tr[tr[son[1][i]].l].k;}for (int i = 1; i <= cnt[0]; ++i) {sum -= tr[son[0][i]].k;lsum -= tr[tr[son[0][i]].l].k;}if (sum < k) return 0;if (lsum >= k) {for (int i = 1; i <= cnt[0]; ++i) {son[0][i] = tr[son[0][i]].l;}for (int i = 1; i <= cnt[1]; ++i) {son[1][i] = tr[son[1][i]].l;}return query(l, mid, k);} else {for (int i = 1; i <= cnt[0]; ++i) {son[0][i] = tr[son[0][i]].r;}for (int i = 1; i <= cnt[1]; ++i) {son[1][i] = tr[son[1][i]].r;}return query(mid+1, r, k-lsum);}
}inline int lowbit(int x) {return x & -x;
}void modify(int x, int k) {int val = arr[x];while (x <= N) {update(root[x], 1, unicnt, val, k);x += lowbit(x);}
}int ask(int l, int r, int k) {cnt[0] = cnt[1] = 0;for (int i = l-1; i; i-=lowbit(i)) {son[0][++cnt[0]] = root[i];}for (int i = r; i; i-=lowbit(i)) {son[1][++cnt[1]] = root[i];}return query(1, unicnt, k);
} void solve(){sc("%d%d", &N, &M);for (int i = 1; i <= N; ++i) {sc("%d", &arr[i]);uniarr[i] = arr[i];}unicnt = N;int l, r, k;char opt[5];for (int i = 1; i <= M; ++i) {sc("%s%d%d", opt, &l, &r);if (*opt == 'Q') {sc("%d", &k);qr[i] = {0, l, r, k};} else {qr[i] = {1, l, r, 0};uniarr[++unicnt] = r;}}sort(uniarr+1, uniarr+1+unicnt);unicnt = unique(uniarr+1, uniarr+1+unicnt) - uniarr - 1;for (int i = 1; i <= N; ++i) {arr[i] = lower_bound(uniarr+1, uniarr+1+unicnt, arr[i]) - uniarr;modify(i, 1);}for (int i = 1; i <= M; ++i) {if (qr[i].opt) {qr[i].r = lower_bound(uniarr+1, uniarr+1+unicnt, qr[i].r) - uniarr;modify(qr[i].l, -1);arr[qr[i].l] = qr[i].r;modify(qr[i].l, 1);} else {pr("%d\n", uniarr[ask(qr[i].l, qr[i].r, qr[i].k)]);}}}signed main()
{#ifndef ONLINE_JUDGE//FILE_INFILE_OUT#endifint T = 1;//cin >> T;while (T--) solve();return AC;
}

整体二分

//P2617
/*@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;int arr[MAX_N];
int uniarr[MAX_N];
int unicnt = 0;struct Qr {int opt, l, r, k, id;
}qr[MAX_N], L[MAX_N], R[MAX_N];int tr[MAX_N];
inline int lowbit(int x) {return x & -x;
}inline void add(int x, int k) {while (x <= N) {tr[x] += k;x += lowbit(x);}
}inline int ask(int x) {int res = 0;while (x) {res += tr[x];x -= lowbit(x);}return res;
}int ans[MAX_N];void div(int l, int r, int x, int y) {if (l > r) return;int mid = l + ((r-l)>>1);int lx = 0;int rx = 0;for (int i = x; i <= y; ++i) {if (qr[i].opt == 0) {if (qr[i].k <= uniarr[mid]) {add(qr[i].l, qr[i].id);L[++lx] = qr[i];} else {R[++rx] = qr[i];}} else {int cnt = ask(qr[i].r) - ask(qr[i].l-1);if (qr[i].k <= cnt) {ans[qr[i].id] = uniarr[mid];L[++lx] = qr[i];} else {qr[i].k -= cnt;R[++rx] = qr[i];}}}for (int i = 1; i <= lx; ++i) {if (L[i].opt == 0) add(L[i].l, -L[i].id);qr[x+i-1] = L[i];}for (int i = 1; i <= rx; ++i) {qr[x+lx-1+i] = R[i];}div(l, mid-1, x, x+lx-1);div(mid+1, r, x+lx, y);
}void solve(){sc("%d%d", &N, &M);int cnt = 0;for (int i = 1; i <= N; ++i) {sc("%d", &arr[i]);qr[++cnt] = {0, i, i, arr[i], 1};uniarr[++unicnt] = arr[i];}int l, r, k;char opt[5];int qcnt = 0;for (int i = 1; i <= M; ++i) {sc("%s%d%d", opt, &l, &r);if (*opt == 'Q') {sc("%d", &k);qr[++cnt] = {1, l, r, k, ++qcnt};} else {uniarr[++unicnt] = r;qr[++cnt] = {0, l, l, arr[l], -1};arr[l] = r;qr[++cnt] = {0, l, l, arr[l], 1};}}sort(uniarr+1, uniarr+unicnt+1);unicnt = unique(uniarr+1, uniarr+1+unicnt) - uniarr - 1;div(1, unicnt, 1, cnt);for (int i = 1; i <= qcnt; ++i) {pr("%d\n", ans[i]);}
}signed main()
{#ifndef ONLINE_JUDGE//FILE_INFILE_OUT#endifint T = 1;//cin >> T;while (T--) solve();return AC;
}

P2617 Dynamic Rankings 动态区间第K大【树套树】或【整体二分】相关推荐

  1. 【BZOJ】1901: Zju2112 Dynamic Rankings(区间第k小+树状数组套主席树)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1901 首先还是吐槽时间,我在zoj交无限tle啊!!!!!!!!我一直以为是程序错了啊啊啊啊啊啊. ...

  2. P2617 Dynamic Rankings 动态主席树

    \(\color{#0066ff}{ 题目描述 }\) 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i ...

  3. 树套树 ---- 树状数组套权值线段树模板题 P2617 Dynamic Rankings 动态第K大

    题目链接 题目大意: 给你一个数组aaa,aaa有两个操作 询问aaa中[l,r][l,r][l,r]区间里面第kkk小的数是哪个? 修改axa_xax​为yyy 解题思路: 首先我们知道权值线段树是 ...

  4. 【HDU - 2665】Kth number(区间第K大,主席树,模板)

    题干: Give you a sequence and ask you the kth big number of a inteval. Input The first line is the num ...

  5. P3332 [ZJOI2013]K大数查询【整体二分】或【树套树】

    传送门 给定一个长度为NNN的可重集合 支持修改,离线 求区间可重集合的并集第K大 这里介绍两种方法[树套树]和 [整体二分] 这里还有个单点修改,有点类似的 P2617 Dynamic Rankin ...

  6. J - [永不止步-2017]_区间第K大-线段树维护

    J - [永不止步-2017]_区间第K大 把线段树的结点的数据域设置为vector类型即可别的操作为区间更新模板 思路就是这样runtime error暂时没改对 #include<bits/ ...

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

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

  8. P2617 Dynamic Rankings(整体二分)

    P2617 Dynamic Rankings 题意: 待修改的区间最值问题 题解: 整体二分天然带有修改性 整体二分做不带修改的区间最值->看这里 现在待修改,我们可以将第l位修改为x,因为我们 ...

  9. LuoguP2617 Dynamic Rankings (动态主席树学习理解)

    题目地址 题目链接 题解 动态主席树的板子题.动态主席树其实和静态的有很大差别,虽然同样是n个根,但是节点并不能共用,每个根节点表示bit上的一段区间. 所以其实是个树套树的东西来着,外层是bit,内 ...

最新文章

  1. 如何写出优雅的 Golang 代码
  2. 在人工智能时代下,如何让券商的数据做到“快准稳”
  3. 关于Java抽象类,接口与实现接口及派生类继承基类
  4. request.getcontextPath() 详解(转)
  5. settings sync 怎么用_用好这7个VS Code插件,前端编程效率蹭蹭涨
  6. 入门嵌入式HTML/CSS/脚本引擎 sciter(问题篇)
  7. 使用STM32固件库开发GD32 汇总
  8. 两台计算机如何共享文档,两台电脑如何共享文件
  9. IMDG产品功能扩展
  10. BCIduino转载|3D打印机使用的日常问题汇总
  11. 【训练题66:状压暴力 | 子集dp】Greater Integer, Better LCM | 2021牛客暑期多校训练营5
  12. Mysql中的循环语句
  13. blackbox_exporter的使用
  14. app软件开发有哪些方式?
  15. 未对文件 C:\Users\Administrator\Documents\WindowsPowerShell\profile.ps1 进行数字签名。无法在当前系统上运行该脚本。
  16. 【ug903】Xilinx XDC约束的序(Order)
  17. 知识汇总二(简单光照模型)
  18. 本地方法栈 程序计数器 方法区
  19. 【BP预测】基于布谷鸟算法优化BP神经网络数据回归预测含Matlab源码
  20. 为什么学微电子的都怕掉进材料的坑?

热门文章

  1. 学生表(Student)、课程表(Course)、成绩表(Score)以及教师信息表(Teacher)...
  2. centos7.4安装中文字体黑体宋体
  3. Linux软链接和硬链接
  4. windows 防火墙配置(只允许外网连接,不允许内网连接)
  5. 征婚网站php免费源码,php交友网站系统正式版php征婚网站模板
  6. Laravel repository数据仓库使用 Star.hou红楼一梦
  7. 【实战】使用Bert微调完成文本二分类
  8. 商标注册需要多久下证
  9. 机器学习基础:信息论
  10. 关于加密通道规范,你真正用的是TLS,而非SSL