[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+线段树+算法设计策略
Posted Lev今天学习了吗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+线段树+算法设计策略相关的知识,希望对你有一定的参考价值。
4552: [Tjoi2016&Heoi2016]排序
Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 1451 Solved: 734
[Submit][Status][Discuss]
Description
在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题
,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排
序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q
位置上的数字。
Input
输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。1 <= n, m <= 10^5第二行为n个整
数,表示1到n的一个全排列。接下来输入m行,每一行有三个整数op, l, r, op为0代表升序排序,op为1代表降序
排序, l, r 表示排序的区间。最后输入一个整数q,q表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5
,1 <= m <= 10^5
Output
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。
Sample Input
6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
Sample Output
5
HINT
Source
Analysis
这道题的正解思路简直清新qwq
第一眼过去肯定就是拿线段树合并分裂强行模拟
但是正解:元素模糊化+二分
(元素模糊化是我定义出来的,,,= =)
首先这道题拿正常线段树做的最大难点非常显然:这个要怎么排序啊
所以根据正解思路,我们将元素模糊处理成0和1,这样排序就变成了 统计+区间赋值
怎么模糊化呢?二分答案 line ,然后对于 ≥line 的设置成 1,否则为 0
这样,把二分需要用到的 check函数 定义为第 q 个元素的 0/1 状态
那么根据我们的定义,这一位肯定是要 ≥line 的(否则就不对劲),以此确定下一步的二分范围
有点反证法的意味啊,,,
Code
1 #include<cstdio> 2 #include<iostream> 3 #define maxn 1000000 4 #define mid (L+R)/2 5 #define lc (rt<<1) 6 #define rc (rt<<1|1) 7 using namespace std; 8 9 int arr[maxn],line,n,m,qwq; 10 11 struct data{ int op,L,R; }ask[maxn]; 12 13 struct node{ 14 int sum,lazy; 15 }T[maxn*4]; 16 17 void maintain(int rt){ T[rt].sum = T[lc].sum+T[rc].sum; } 18 19 void pushdown(int rt,int L,int R){ 20 if(!T[rt].lazy) return; 21 22 T[lc].sum = (T[rt].lazy-1)*(mid-L+1); 23 T[rc].sum = (T[rt].lazy-1)*(R-mid); 24 T[lc].lazy = T[rc].lazy = T[rt].lazy; 25 T[rt].lazy = 0; 26 } 27 28 void build(int rt,int L,int R){ 29 T[rt].sum = T[rt].lazy = 0; 30 if(L == R) T[rt].sum = (arr[L]>=line?1:0); 31 else{ 32 build(lc,L,mid); build(rc,mid+1,R); 33 maintain(rt); 34 } 35 } 36 37 int query(int rt,int L,int R,int qL,int qR){ 38 pushdown(rt,L,R); 39 if(qL <= L && R <= qR) return T[rt].sum; 40 else{ 41 int ans = 0; 42 if(qL <= mid) ans += query(lc,L,mid,qL,qR); 43 if(qR > mid) ans += query(rc,mid+1,R,qL,qR); 44 return ans; 45 } 46 } 47 48 int query(int rt,int L,int R,int pos){ 49 pushdown(rt,L,R); 50 // if(pos < L || R < pos) return -1; 51 if(L == R) return T[rt].sum; 52 else{ 53 if(pos <= mid) return query(lc,L,mid,pos); 54 else return query(rc,mid+1,R,pos); 55 } 56 } 57 58 void modify(int rt,int L,int R,int qL,int qR,int val){ 59 pushdown(rt,L,R); 60 if(qL > qR) return; 61 if(qL <= L && R <= qR){ 62 T[rt].sum = val*(R-L+1); T[rt].lazy = val+1; 63 }else{ 64 if(qL <= mid) modify(lc,L,mid,qL,qR,val); 65 if(qR > mid) modify(rc,mid+1,R,qL,qR,val); 66 maintain(rt); 67 } 68 } 69 70 void SORT(int op,int L,int R){ 71 int sum = query(1,1,n,L,R); 72 // printf("#%d: sum%d\n",op,sum); 73 if(op){ 74 modify(1,1,n,L,L+sum-1,1); 75 modify(1,1,n,L+sum,R,0); 76 }else{ 77 modify(1,1,n,L,R-sum,0); 78 modify(1,1,n,R-sum+1,R,1); 79 } 80 } 81 82 bool check(){ 83 build(1,1,n); 84 // cout << "Now...."; for(int i = 1;i <= n;i++) printf("%d ",query(1,1,n,i)); cout << endl; 85 for(int i = 1;i <= m;i++){ 86 SORT(ask[i].op,ask[i].L,ask[i].R); 87 // printf("#%d Now..",i); for(int j = 1;j <= n;j++) printf("%d ",query(1,1,n,j)); cout << endl; 88 } 89 return query(1,1,n,qwq); 90 } 91 92 int main(){ 93 scanf("%d%d",&n,&m); 94 95 for(int i = 1;i <= n;i++) scanf("%d",&arr[i]); 96 for(int i = 1;i <= m;i++) scanf("%d%d%d",&ask[i].op,&ask[i].L,&ask[i].R); 97 98 scanf("%d",&qwq); 99 100 int L = 1,R = n; 101 while(L < R){ 102 line = (L+R+1)/2; 103 if(!check()) R = line-1; 104 else L = line; 105 // printf("[%d,%d]: line%d --=",L,R,line); 106 // for(int i = 1;i <= n;i++) printf("%d ",query(1,1,n,i)); cout << endl; 107 } 108 109 cout << R; 110 111 return 0; 112 }
以上是关于[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+线段树+算法设计策略的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ4552:[Tjoi2016&Heoi2016]排序
[BZOJ4552][TJOI2016&&HEOI2016]排序
BZOJ4552:[HEOI2016/TJOI2016]排序——题解
[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+线段树+算法设计策略