题意

一个 \(1\) 到 \(n\) 的全排列,\(m\) 种操作,每次将一段区间 \([l,r]\) 按升序或降序排列,求 \(m\) 次操作后的第 \(k\) 位。

\(1 \leq n \leq 10^5\)

思路

两个 \(\log\) 的做法展现了二分答案的强大功能。首先二分枚举第 \(k\) 位的值,然后将小于等于它的数都变为 \(1\) ,大于它的数变为 \(0\) ,线段树可以实现对 \(01\) 序列快速的排序,按要求进行排序,然后如果第 \(k\) 位为 \(1\) 说明这个数小于等于 \(k\) ,就这样不断二分下来,得到的边界值就是第 \(k\) 位真实的值。这个做法是离线的,有两个 \(\log\) ,但代码好实现。

但这道题,有一个 \(\log\) 、在线的做法。考虑每个位置开一棵动点线段树,把这个位置的数扔进线段树,区间的排序直接用线段树合并进行,但是如果区间的某个端点落在某一个完整的区间内,那就会破坏这个区间的单调性,所以还要线段树分割。我们对于一个完整区间,存下是升序还是降序,然后“分割”出需要的元素,线段树分割代码如下:

  1. void split(int &x,int y,int K,int l,int r) //y拆前K个给x,合并前将初始x清零(x是一个空树)
  2. {
  3. create(x);
  4. if(l==r){sum[x]=sum[y],sum[y]=0;return;}
  5. int mid=(l+r)>>1;
  6. if(K<=sum[lson[y]])
  7. {
  8. split(lson[x],lson[y],K,l,mid);
  9. sum[x]=sum[lson[x]]+sum[rson[x]];
  10. sum[y]=sum[lson[y]]+sum[rson[y]];
  11. return;
  12. }
  13. split(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
  14. lson[x]=lson[y],lson[y]=0;
  15. sum[x]=sum[lson[x]]+sum[rson[x]];
  16. sum[y]=sum[lson[y]]+sum[rson[y]];
  17. }

和线段树合并的写法大致相同。

初始有 \(n\log n\) 个点,每次操作最多分割出 \(2\log n\) 个节点 ,所以空间复杂度为 \(O(n\log n)\)。

合并初始的 \(n\) 个节点有一个 \(n\log n\) ,而分割的节点也最多是 \(2 n\log n\) ,所以时间复杂度也是 \(O(n\log n)\)。

代码

  1. #include<bits/stdc++.h>
  2. #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
  3. #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
  4. typedef long long LL;
  5. using namespace std;
  6. const int N=1e5+5;
  7. const int NN=N*60;
  8. bool mmr1;
  9. int sum[NN],lson[NN],rson[NN];
  10. int rt[N],tot;
  11. void build()
  12. {
  13. memset(rt,0,sizeof(rt));
  14. sum[tot=0]=lson[0]=rson[0]=0;
  15. }
  16. void create(int &k){if(!k)k=++tot,sum[k]=lson[k]=rson[k]=0;}
  17. void update(int &k,int x,int l,int r)
  18. {
  19. create(k);
  20. sum[k]++;
  21. if(l==r)return;
  22. int mid=(l+r)>>1;
  23. if(x<=mid)update(lson[k],x,l,mid);
  24. else update(rson[k],x,mid+1,r);
  25. }
  26. int query1(int k,int K,int l,int r)
  27. {
  28. if(l==r)
  29. {
  30. if(sum[k]!=1)return -1;
  31. return l;
  32. }
  33. int mid=(l+r)>>1;
  34. if(K<=sum[lson[k]])return query1(lson[k],K,l,mid);
  35. else return query1(rson[k],K-sum[lson[k]],mid+1,r);
  36. }
  37. int query2(int k,int K,int l,int r)
  38. {
  39. if(l==r)
  40. {
  41. if(sum[k]!=1)return -1;
  42. return l;
  43. }
  44. int mid=(l+r)>>1;
  45. if(K<=sum[rson[k]])return query2(rson[k],K,mid+1,r);
  46. else return query2(lson[k],K-sum[rson[k]],l,mid);
  47. }
  48. void merge(int &x,int y,int l,int r) //y并进x
  49. {
  50. if(!x||!y){x=(x|y);return;}
  51. if(l==r){sum[x]+=sum[y];return;}
  52. int mid=(l+r)>>1;
  53. merge(lson[x],lson[y],l,mid);
  54. merge(rson[x],rson[y],mid+1,r);
  55. sum[x]=sum[lson[x]]+sum[rson[x]];
  56. }
  57. void split1(int &x,int y,int K,int l,int r) //y拆前K个给x
  58. {
  59. create(x);
  60. if(l==r){sum[x]=sum[y],sum[y]=0;return;}
  61. int mid=(l+r)>>1;
  62. if(K<=sum[lson[y]])
  63. {
  64. split1(lson[x],lson[y],K,l,mid);
  65. sum[x]=sum[lson[x]]+sum[rson[x]];
  66. sum[y]=sum[lson[y]]+sum[rson[y]];
  67. return;
  68. }
  69. split1(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
  70. lson[x]=lson[y],lson[y]=0;
  71. sum[x]=sum[lson[x]]+sum[rson[x]];
  72. sum[y]=sum[lson[y]]+sum[rson[y]];
  73. }
  74. void split2(int &x,int y,int K,int l,int r) //y拆后K个给x
  75. {
  76. create(x);
  77. if(l==r){sum[x]=sum[y],sum[y]=0;return;}
  78. int mid=(l+r)>>1;
  79. if(K<=sum[rson[y]])
  80. {
  81. split2(rson[x],rson[y],K,mid+1,r);
  82. sum[x]=sum[lson[x]]+sum[rson[x]];
  83. sum[y]=sum[lson[y]]+sum[rson[y]];
  84. return;
  85. }
  86. split2(lson[x],lson[y],K-sum[rson[y]],l,mid);
  87. rson[x]=rson[y],rson[y]=0;
  88. sum[x]=sum[lson[x]]+sum[rson[x]];
  89. sum[y]=sum[lson[y]]+sum[rson[y]];
  90. }
  91. set<int>st;
  92. set<int>::iterator it,it1;
  93. bool f[N];
  94. int find_leftmost(int x)
  95. {
  96. it=st.upper_bound(x);
  97. return *--it;
  98. }
  99. int find_rightmost(int x)
  100. {
  101. it=st.upper_bound(x);
  102. return (*it)-1;
  103. }
  104. bool mmr2;
  105. int main()
  106. {
  107. int T,n,m,K;
  108. scanf("%d",&T);
  109. while(T--)
  110. {
  111. build();
  112. st.clear();
  113. memset(f,0,sizeof(f));
  114. scanf("%d%d",&n,&m);
  115. FOR(i,1,n)
  116. {
  117. int x;
  118. scanf("%d",&x);
  119. update(rt[i],x,1,n);
  120. }
  121. FOR(i,1,n+1)st.insert(i);
  122. while(m--)
  123. {
  124. int op,l,r;
  125. scanf("%d%d%d",&op,&l,&r);
  126. int L=find_leftmost(l);
  127. if(l!=L)
  128. {
  129. if(f[L]==0)rt[l]=0,split1(rt[l],rt[L],l-L,1,n);
  130. else rt[l]=0,split2(rt[l],rt[L],l-L,1,n);
  131. swap(rt[l],rt[L]);
  132. f[l]=f[L];
  133. st.insert(l);
  134. }
  135. int R=find_rightmost(r),_R=find_leftmost(r);
  136. if(r!=R)
  137. {
  138. f[r+1]=f[_R];
  139. if(f[_R]==0)rt[r+1]=0,split2(rt[r+1],rt[_R],R-r,1,n);
  140. else rt[r+1]=0,split1(rt[r+1],rt[_R],R-r,1,n);
  141. st.insert(r+1);
  142. }
  143. f[l]=op;
  144. it=st.find(l),it++;
  145. while((*it)<=r)
  146. {
  147. merge(rt[l],rt[*it],1,n);
  148. it1=it,it++,st.erase(it1);
  149. }
  150. }
  151. scanf("%d",&K);
  152. int x=find_leftmost(K);
  153. if(f[x]==0)printf("%d\n",query1(rt[x],K-x+1,1,n));
  154. else printf("%d\n",query2(rt[x],K-x+1,1,n));
  155. }
  156. return 0;
  157. }

HDU 5649 DZY Loves Sorting(二分答案+线段树/线段树合并+线段树分割)的更多相关文章

  1. hdu 5649 DZY Loves Sorting 二分+线段树

    题目链接 给一个序列, 两种操作, 一种是将[l, r]里所有数升序排列, 一种是降序排列. 所有操作完了之后, 问你a[k]等于多少. 真心是涨见识了这题..好厉害. 因为最后只询问一个位置, 所以 ...

  2. 数据结构(线段树):HDU 5649 DZY Loves Sorting

    DZY Loves Sorting Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Oth ...

  3. HDU 5649.DZY Loves Sorting-线段树+二分-当前第k个位置的数

    DZY Loves Sorting Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Oth ...

  4. hdu 5646 DZY Loves Partition 二分+数学分析+递推

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=5646 题意:将n分成k个正整数之和,要求k个数全部相同:并且这k个数的乘积最大为多少?结果mod 1e^9 ...

  5. BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列

    BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列 题意: 分析: 拆成链,二分答案,奇偶两个单调队列维护最大子段和,记录方案. 代码: #include <cstdio&g ...

  6. hdu 5195 DZY Loves Topological Sorting 线段树+拓扑排序

    DZY Loves Topological Sorting Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/sho ...

  7. hdu 5195 DZY Loves Topological Sorting BestCoder Round #35 1002 [ 拓扑排序 + 优先队列 || 线段树 ]

    传送门 DZY Loves Topological Sorting Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131 ...

  8. hdu 5195 DZY Loves Topological Sorting (拓扑排序+线段树)

    DZY Loves Topological Sorting Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 ...

  9. HDU 5646 DZY Loves Partition 数学 二分

    DZY Loves Partition 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5646 Description DZY loves parti ...

随机推荐

  1. Python学习记录之----网络通信(二)

    网络通信   socket 这一节太难了,还是看TA的吧 http://www.cnblogs.com/alex3714/articles/5830365.html 不能执行top等类似的 会持续输出 ...

  2. kalinux 换源

    1.系统使用第一步建议先换源,否则将出现很多未知问题 #以下两个2选1,打开要编辑的源 sudo leafpad /etc/apt/sources.list sudo gedit /etc/apt/s ...

  3. 网易新网 spider

    # -*- coding: utf-8 -*- import os import sys import urllib.request import requests import re from lx ...

  4. C# 声明隐式类型的局部变量

    在c#中赋值给变量的值必须具有和变量相同的类型.如int值赋给int变量,c#编译器可以迅速判断变量初始化表达式的类型,如果变量类型不符,就会明确告诉你. 提示需要强制转换(例如在char中不允许使用 ...

  5. python内置函数的简单使用和介绍

    """内置函数的简单使用和介绍参考链接:https://docs.python.org/3/library/functions.html ""&quo ...

  6. 前端框架VUE----表单输入绑定

    vue的核心:声明式的指令和数据的双向绑定. 那么声明式的指令,已经给大家介绍完了.接下来我们来研究一下什么是数据的双向绑定? 另外,大家一定要知道vue的设计模式:MVVM M是Model的简写,V ...

  7. JS3D效果

    <!DOCTYPE html> <html> <head> <title></title> <meta charset="u ...

  8. PS火焰文字制作

    火焰文字制作: 最终效果 第一步: 新建图层,并输入文字(这里不做详细解说)

  9. MyEclipse如何清除废弃的工作空间

    1.MyEclipse如何清除废弃的工作空间Windows--->Preferences--->General--->Startup and Shutdown--->Works ...

  10. org.I0Itec.zkclient.exception.ZkTimeoutException: Unable to connect to zookeeper server within

    org.I0Itec.zkclient.exception.ZkTimeoutException: Unable to connect to zookeeper server within timeo ...