今天学习了一个奇技淫巧--整体二分。关于整体二分的一些理论性的东西,可以参见XRH的《浅谈数据结构题的几个非经典解法》。然后下面是一些个人的心得体会吧,写下来希望加深一下自己的理解,或者如果有人看了或许也有些帮助。

ZOJ2112是一道典型的带修改的区间第k大的问题,有一些树套树等的数据结构可以在线处理这样的问题。但是当题目并不要求在线处理的时候,其实我们可以选择一下整体二分的思想。

个人对整体二分的理解是这样子的,首先对于修改,即把a[xi]=yi(1<=yi<=C)的时候,我们可以把修改的操作划分成两部分,一部分是yi<=mid,另一部分是yi>mid。首先我们忽略掉yi>mid的操作,将yi<=mid以及询问操作按照输入的顺序执行一遍,这样我们就可以知道<=mid的操作对所有询问的贡献,接下来我们就要根据贡献对修改操作划分成两部分,一部分是答案在<=mid里面的,另外一部分是答案在>mid里面的,对于<=mid的,显然我们可以递归求解(因为>mid的操作对那些<=mid是没有影响的),而对于>mid的来说,<=mid的操作是有影响的,但是<=mid所造成的影响已经算过一遍了,所以对于>mid的来说,其实也是独立的。所以如果我们把答案二分的范围的大小设为C,操作的总数设为n,那么划分到<=mid的操作是n1,>mid的操作是n2时(n1+n2=n)

有 T(n,C)=T(n1,C/2)+T(n2,C/2)+O(nlogC) T(n)=nlogC^2...(也不知道复杂度对不对,可以画一个图来YY一下)

好吧,其实我也不知道上面说了什么,感觉真的蛮难用一两段话将整个思路说出来的。其实总的抽象的来说,就是一开始维护的可能答案的区间是[l,r],

然后我们将所有<=mid的操作作一遍,然后将答案落在[l,mid]的操作(询问和修改)划到一边,将答案落在[mid+1,r]的操作(询问和修改)划到另一边。

  1. #pragma warning(disable:4996)
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <iostream>
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <string>
  8. #include <vector>
  9. #include <queue>
  10. using namespace std;
  11.  
  12. #define maxn 300000
  13.  
  14. struct Query
  15. {
  16. int x, y; // qt==3 [x,y] qt==1||2 a[x]=y
  17. int qt; // query type 1 for increase 2 for decrease 3 for query
  18. int cur; // the current contribution
  19. int k; // the query is the k-th smalleset
  20. int index;
  21. }q[maxn];
  22. int qtop;
  23.  
  24. int a[maxn];
  25. int b[maxn];
  26. int bsize;
  27. int n, m;
  28. int tot;
  29. int ans[maxn];
  30. int ansid;
  31.  
  32. int tmp[maxn];
  33. Query q1[maxn], q2[maxn];
  34. int bit[maxn];
  35. void add(int x, int v)
  36. {
  37. while (x <= n){
  38. bit[x] += v;
  39. x += x&(-x);
  40. }
  41. }
  42.  
  43. int query(int x)
  44. {
  45. int ret = 0;
  46. while (x > 0){
  47. ret += bit[x];
  48. x -= x&(-x);
  49. }
  50. return ret;
  51. }
  52.  
  53. void solve(int head, int tail, int l, int r)
  54. {
  55. if (head > tail) return;
  56. if (l == r){
  57. for (int i = head; i <= tail; ++i){
  58. if (q[i].qt == 3) ans[q[i].index] = l;
  59. }
  60. return;
  61. }
  62. int mid = (l + r) >> 1;
  63. // 将所有<=mid的操作作一遍,tmp[i]存的是[head,tail]里<=mid的操作对询问的贡献
  64. for (int i = head; i <= tail; ++i){
  65. if (q[i].qt == 1 && q[i].y <= mid){
  66. add(q[i].x, 1);
  67. }
  68. else if (q[i].qt == 2 && q[i].y <= mid){
  69. add(q[i].x, -1);
  70. }
  71. else{
  72. tmp[i] = query(q[i].y) - query(q[i].x - 1);
  73. }
  74. }
  75. // 将操作撤销一下
  76. for (int i = head; i <= tail; ++i){
  77. if (q[i].qt == 1 && q[i].y <= mid){
  78. add(q[i].x, -1);
  79. }
  80. else if (q[i].qt == 2 && q[i].y <= mid){
  81. add(q[i].x, 1);
  82. }
  83. }
  84. // 将操作划分成两部分
  85. int l1=0, l2 = 0;
  86. for (int i = head; i <= tail; ++i){
  87. if (q[i].qt == 3){
  88. // 如果前面的数加上当前的数>=q[i].k,说明该询问的可行区间在[l,mid],往左划分
  89. if (q[i].cur + tmp[i] >= q[i].k){
  90. q1[++l1] = q[i];
  91. }
  92. else{
  93. // 否则往右划分,并记下贡献
  94. q[i].cur += tmp[i];
  95. q2[++l2] = q[i];
  96. }
  97. }
  98. else{
  99. if (q[i].y <= mid) q1[++l1] = q[i];
  100. else q2[++l2] = q[i];
  101. }
  102. }
  103.  
  104. for (int i = 1; i <= l1; ++i) {
  105. q[head + i - 1] = q1[i];
  106. }
  107. for (int i = 1; i <= l2; ++i){
  108. q[head + l1 + i - 1] = q2[i];
  109. }
  110. solve(head, head + l1 - 1, l, mid);
  111. solve(head + l1, tail, mid + 1, r);
  112. }
  113.  
  114. int main()
  115. {
  116. int T; cin >> T;
  117. while (T--)
  118. {
  119. scanf("%d%d", &n, &m);
  120. qtop = 0;tot = 0;
  121. for (int i = 1; i <= n; ++i){
  122. scanf("%d", a + i);
  123. b[tot++] = a[i];
  124. q[++qtop].qt = 1;
  125. q[qtop].x = i;
  126. q[qtop].y = a[i];
  127. }
  128. char cmd[4];
  129. int xi, yi,ki;
  130. ansid = 0;
  131. for (int i = 0; i < m; ++i){
  132. scanf("%s", cmd);
  133. if (cmd[0] == 'Q'){
  134. scanf("%d%d%d", &xi, &yi, &ki);
  135. q[++qtop].x = xi; q[qtop].y = yi; q[qtop].k = ki;
  136. q[qtop].qt = 3; q[qtop].cur = 0; q[qtop].index = ++ansid;
  137. }
  138. else{
  139. scanf("%d%d", &xi, &yi);
  140. q[++qtop].x = xi; q[qtop].y = a[xi]; q[qtop].qt = 2;
  141. q[++qtop].x = xi; q[qtop].y = yi; q[qtop].qt = 1;
  142. a[xi] = yi;
  143. b[tot++] = yi;
  144. }
  145. }
  146. sort(b, b + tot);
  147. bsize = unique(b, b + tot) - b;
  148.  
  149. for (int i = 1; i <= qtop; ++i){
  150. if (q[i].qt == 1 || q[i].qt == 2){
  151. q[i].y = lower_bound(b, b + bsize, q[i].y) - b + 1;
  152. }
  153. }
  154. solve(1, qtop, 1, bsize);
  155. for (int i = 1; i <= ansid; ++i){
  156. printf("%d\n", b[ans[i]-1]);
  157. }
  158. }
  159. //system("pause");
  160. return 0;
  161. }

ZOJ2112 Dynamic Rankings(整体二分)的更多相关文章

  1. [bzoj1901][zoj2112][Dynamic Rankings] (整体二分+树状数组 or 动态开点线段树 or 主席树)

    Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has ...

  2. 【BZOJ1901】Dynamic Rankings [整体二分]

    Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一个含 ...

  3. BZOJ 1901 Zju2112 Dynamic Rankings ——整体二分

    [题目分析] 上次用树状数组套主席树做的,这次用整体二分去水. 把所有的查询的结果一起进行二分,思路很好. [代码] #include <cstdio> #include <cstr ...

  4. BZOJ1901: Zju2112 Dynamic Rankings(整体二分 树状数组)

    Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 9094  Solved: 3808[Submit][Status][Discuss] Descript ...

  5. BZOJ 1901 Dynamic Rankings (整体二分+树状数组)

    题目大意:略 洛谷传送门 这道题在洛谷上数据比较强 貌似这个题比较常见的写法是树状数组套主席树,动态修改 我写的是整体二分 一开始的序列全都视为插入 对于修改操作,把它拆分成插入和删除两个操作 像$C ...

  6. 洛谷$P2617\ Dynamic\ Rankings$ 整体二分

    正解:整体二分 解题报告: 传送门$w$ 阿查询带修区间第$k$小不显然整体二分板子呗,,, 就考虑先按时间戳排序(,,,其实并不需要读入的时候就按着时间戳排的鸭$QwQ$ 每次二分出$mid$先把所 ...

  7. BZOJ.1901.Dynamic Rankings(整体二分)

    题目链接 BZOJ 洛谷 (以下是口胡) 对于多组的询问.修改,我们可以发现: 假设有对p1,p2,p3...的询问,在这之前有对p0的修改(比如+1),且p0<=p1,p2,p3...,那么我 ...

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

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

  9. ZOJ2112 Dynamic Rankings

    题意 Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings ...

随机推荐

  1. Dapper基础增删查改、事务和存储过程

    1.前言 Dapper是一个轻量级的orm框架,上手也非常的简单,它可以实体映射,所以先准备实体如下: public class Couser { public int id { get; set; ...

  2. global js库

    var GLOBAL = {}; GLOBAL.namespace = function(str) { var arr = str.split("."), o = GLOBAL,i ...

  3. Python 协程与事件循环

    Table of Contents 前言 协程 async & await 事件循环 asyncio 的事件循环 结语 参考链接 前言 Python 标准库 asyncio 是我目前接触过的最 ...

  4. python-成员修饰符

    python的面相对象中,拥有3个成员,字段.方法.属性 class Foo: def __init__(self,name): #公有字段name在类中与类外均能调用 self.name = nam ...

  5. Python学习-django-ModelForm组件

    ModelForm a. class Meta: model, # 对应Model的 fields=None, # 字段 exclude=None, # 排除字段 labels=None, # 提示信 ...

  6. Windows后续处理工作

    1.远程桌面开启,应预先开启windows防火墙,并放行“远程桌面”(TCP 3389)端口,防止用户自行开启防火墙时操作错误. 2.防火墙高级安全-需放行ICMP 3.补丁更新,更新完重启 4.本地 ...

  7. leetcode_day02

    任务二:删除排序数组中的重复项 原文链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ 最开始的解决思路: ...

  8. Homework-09 二维数组动态显示

    思路 主要是把计算的函数由一次跑完全部改成一次一步 主要是把一个3重for循环改为能一步一次的实现 public int nstep(int n){ while((n--)!=0){ //n步长 // ...

  9. error MSB6006: “aapt.exe”已退出,代码为-1073741819

    今天升级了Xamarin和Android SDK之后连模板程序生成都报这个错误,真是想剁手啊,最后在google同学的帮助下搜索到了Xamarin官方论坛上的回答 这个问题是生成工具版本选择的问题,似 ...

  10. 第十二篇:HTML基础

    本篇内容 HTML概述 HTML常用基本标签 CSS格式引入 一. HTML概述 1.定义: HTML,超文本标记语言,写给浏览器的语言,目前网络上应用最广泛的语言.HTML也在不断的更新,最新版本已 ...