Dynamic Rankings(树状数组套权值线段树)

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。

对于每一个询问指令,你必须输出正确的回答。有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。

这道题就是一个带修区间第k大的问题,可以用cdq分治做,也可以用树套树做。由于有修改,如果暴力在主席树上修改,时间复杂度就爆炸了,单个修改都是\(O(nlogn)\)的。再看看查询,是\(O(logn)\)的,这就启发我们可不可以把它们两个平均一下,于是就用到了树状数组。在树状数组的每个区间上都建一颗权值线段树,维护此区间上每个值出现的个数(链+权值线段树=主席树,树状数组+权值线段树=带修主席树【滑稽】)。那么修改某一个数的值,会牵动到树状数组上logn个区间,然后权值线段树单点修改时间为\(O(logn)\),总时间是\(O(log^2nn)\)的。查询区间\([l, r]\)的第k大值,需要得到\([1, l-1]\)和\([1, r]\)的权值线段树,然后将它们相减,依然会牵动到logn个区间,由于权值线段树区间查询时间是\(O(logn)\),总时间也是\(O(logn^2n)\)的。注意要离散化,这题还是挺难写的。

所以说,数据结构可以平衡操作的时间复杂度。

P.S. 三个月后的我看到这篇博客仿佛看到了救星(要讲课啊妈蛋)

P.A. 四个月后的我忽然发现这个东西的本质和主席树的本质是不同的,因为主席树的每棵权值线段树是继承与前一棵权值线段树的,而它只是把插入和查询的前提复杂度变成了\(O(logn)\)而已。不过,我还是倾向于叫它带修主席树,因为它是主席树应用到树状数组上的自然推论。

  1. #include <cctype>
  2. #include <cstdio>
  3. #include <algorithm>
  4. using namespace std;
  5. const int maxn=2e4+5;
  6. int n, m, a[maxn], b[maxn], cntb, rt[maxn];
  7. int c1[maxn], c2[maxn], c3[maxn];
  8. int size[maxn*400], cseg, ls[maxn*400], rs[maxn*400];
  9. int tset1[maxn], tset2[maxn], cnt1, cnt2;
  10. inline int lowbit(int x){ return x&(-x); } //树状数组上的权值线段树
  11. void ins(int &now, int l, int r, int pos, int v){
  12. if (!now) now=++cseg; size[now]+=v;
  13. if (l==r) return; int mid=(l+r)>>1;
  14. if (pos<=mid) ins(ls[now], l, mid, pos, v);
  15. else ins(rs[now], mid+1, r, pos, v);
  16. }
  17. void add(int x, int v){
  18. int k=lower_bound(b+1, b+cntb+1, a[x])-b;
  19. for (int i=x; i<=n; i+=lowbit(i))
  20. ins(rt[i], 1, cntb, k, v);
  21. }
  22. int query(int l, int r, int v){ //logn个权值线段树一起跳
  23. if (l==r) return l;
  24. int sum=0, mid=(l+r)>>1;
  25. for (int i=1; i<=cnt1; ++i) sum-=size[ls[tset1[i]]];
  26. for (int i=1; i<=cnt2; ++i) sum+=size[ls[tset2[i]]];
  27. if (v<=sum){
  28. for (int i=1; i<=cnt1; ++i) tset1[i]=ls[tset1[i]];
  29. for (int i=1; i<=cnt2; ++i) tset2[i]=ls[tset2[i]];
  30. return query(l, mid, v);
  31. } else {
  32. for (int i=1; i<=cnt1; ++i) tset1[i]=rs[tset1[i]];
  33. for (int i=1; i<=cnt2; ++i) tset2[i]=rs[tset2[i]];
  34. return query(mid+1, r, v-sum);
  35. }
  36. }
  37. inline void get(int &x){
  38. char c; int flag=1;
  39. for (; !isdigit(c=getchar()); ) if (c=='-') flag=-1;
  40. for (x=c-48; c=getchar(), isdigit(c); )
  41. x=(x<<3)+(x<<1)+c-48;
  42. if (flag==-1) x=-x;
  43. }
  44. int main(){
  45. get(n); get(m); char c;
  46. for (int i=1; i<=n; ++i) get(a[i]), b[++cntb]=a[i];
  47. for (int i=0; i<m; ++i){
  48. while (!isgraph(c=getchar()));
  49. get(c1[i]); get(c2[i]);
  50. if (c=='Q') get(c3[i]); else b[++cntb]=c2[i];
  51. }
  52. sort(b+1, b+cntb+1); cntb=unique(b+1, b+cntb+1)-b-1;
  53. for (int i=1; i<=n; ++i) add(i, 1);
  54. for (int i=0; i<m; ++i) if (c3[i]){
  55. cnt1=cnt2=0; //把要查询的区间都标出来
  56. for (int j=c1[i]-1; j; j-=lowbit(j)) tset1[++cnt1]=rt[j];
  57. for (int j=c2[i]; j; j-=lowbit(j)) tset2[++cnt2]=rt[j];
  58. printf("%d\n", b[query(1, cntb, c3[i])]);
  59. } else { add(c1[i], -1); a[c1[i]]=c2[i]; add(c1[i], 1); }
  60. return 0;
  61. }

Dynamic Rankings(树状数组套权值线段树)的更多相关文章

  1. [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)

    [BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...

  2. BZOJ2141排队——树状数组套权值线段树(带修改的主席树)

    题目描述 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们的身高有所区别 ...

  3. luogu3380/bzoj3196 二逼平衡树 (树状数组套权值线段树)

    带修改区间K大值 这题有很多做法,我的做法是树状数组套权值线段树,修改查询的时候都是按着树状数组的规则找出那log(n)个线段树根,然后一起往下做 时空都是$O(nlog^2n)$的(如果离散化了的话 ...

  4. CF1093E Intersection of Permutations 树状数组套权值线段树

    \(\color{#0066ff}{ 题目描述 }\) 给定整数 \(n\) 和两个 \(1,\dots,n\) 的排列 \(a,b\). \(m\) 个操作,操作有两种: \(1\ l_a\ r_a ...

  5. 【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings

    谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结 ...

  6. 刷题总结——骑士的旅行(bzoj4336 树链剖分套权值线段树)

    题目: Description 在一片古老的土地上,有一个繁荣的文明. 这片大地几乎被森林覆盖,有N座城坐落其中.巧合的是,这N座城由恰好N-1条双 向道路连接起来,使得任意两座城都是连通的.也就是说 ...

  7. 【bzoj3065】带插入区间K小值 替罪羊树套权值线段树

    题目描述 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它的随从伏特提出 ...

  8. 「ZJOI2017」树状数组(二维线段树)

    「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...

  9. HDU 1934 树状数组 也可以用线段树

    http://acm.hdu.edu.cn/showproblem.php?pid=1394 或者是我自己挂的专题http://acm.hust.edu.cn/vjudge/contest/view. ...

随机推荐

  1. C#实现文件拖放并打开文件(使用ListBox)

    1.C#实现文件拖放并打开文件 (http://www.cnblogs.com/GaoHuhu/archive/2012/10/10/2717954.html)

  2. algorithm之排序算法--待解决

    简述:排序算法,参见http://www.cplusplus.com/reference/algorithm/?kw=algorithm 待解决问题:各种排序算法的实现 /* template < ...

  3. How to reduce Index size on disk?减少ES索引大小的一些小手段

    ES索引文件瘦身总结如下: 原始数据:(1)学习splunk,原始data存big string(2)原始文件还可以再度压缩倒排索引:(1)去掉不必要的倒排索引信息:例如文件位置倒排._source和 ...

  4. Java的反射机制(应用篇)

    Java的的反射机制,是一个很难但却比较有用的概念.反射机制经常出现在框架设计中,大神说:反射是框架设计的灵魂,也就是说要想看懂框架的源代码,必须得掌握反射机制. 作为初学者的我,觉得至少应该掌握它日 ...

  5. json 文件解析与应用

    第一步:首先弄一个 json 文件   我这里成为 config.json 内容如下 { ": { , "desc":"中华人民共和国" }, &qu ...

  6. fswebcam 获取图片

    /************************************************************************* * fswebcam 获取图片 * 说明: * 通 ...

  7. HDU1370Biorhythms(中国剩余定理||暴力)

    Some people believe that there are three cycles in a person's life that start the day he or she is b ...

  8. [BALTIC 2008] Grid

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1169 [算法] 首先DFS枚举出横着切的 然后二分 + 贪心即可 时间复杂度 : O ...

  9. 关于web中注册倒数的问题(亲测)

    <title></title>    <script type="text/javascript">        var leftSecond ...

  10. WSGI 简介(使用python描述)

    WSGI 简介 背景 Python Web 开发中,服务端程序可以分为两个部分,一是服务器程序,二是应用程序.前者负责把客户端请求接收,整理,后者负责具体的逻辑处理.为了方便应用程序的开发,我们把常用 ...