bzoj4552 [Tjoi2016&Heoi2016]排序 (线段树+二分)

题意:一个1~n的排列,m个操作:

0 x y:将ax~ay按升序排列;

1 x y:将ax~ay按降序排列。

询问m次操作后第aq的值。

输入:第一行:两个正整数n,m,表示序列的长度与询问的个数;

第二行:一个1~n的排列;

第3~m+2行:每行一个操作。

第m+3行:一个数q表示询问的位置。

输出:一个数表示aq的值。

样例输入:

6 3

1 6 2 5 3 4

0 1 4

1 3 6

0 2 4

3

样例输出:

5

解析:考虑二分答案,将小于答案的数变为0,将大于等于答案的变为1,这样整个序列就变成了一个01序列。后对于每个二分出来的01序列建一个线段树,由于数列由0和1组成,所以在每次操作时,若为变为升序,则将0往前放,将1往后放,反之亦然。

代码如下:

 1 #include<cstdio>
 2 #define lc o<<1
 3 #define rc o<<1|1
 4 using namespace std;
 5
 6 const int MAXN=100010;
 7 int n,m,a[MAXN],bj[MAXN],x[MAXN],y[MAXN],inx,b[MAXN],sum[MAXN*4],set[MAXN*4];
 8
 9 int read(void) {
10     char c; while (c=getchar(),c<‘0‘ || c>‘9‘); int x=c-‘0‘;
11     while (c=getchar(),c>=‘0‘ && c<=‘9‘) x=x*10+c-‘0‘; return x;
12 }
13
14 void build(int o,int l,int r) { //建树
15     set[o]=-1;
16     if (l==r) {
17       sum[o]=b[l]==1; return;
18     }
19     int mid=l+r>>1;
20     build(lc,l,mid); build(rc,mid+1,r);
21     sum[o]=sum[lc]+sum[rc];
22 }
23
24 void pushdown(int o,int l,int r) { //标记下方
25     set[lc]=set[rc]=set[o]; set[o]=-1;
26     int mid=l+r>>1;
27     sum[lc]=set[lc]*(mid-l+1);
28     sum[rc]=set[rc]*(r-mid);
29 }
30
31 int query_range(int o,int l,int r,int ql,int qr) { //区间查询1的个数
32     if (ql<=l && qr>=r) return sum[o];
33     int mid=l+r>>1,ans=0;
34     if (set[o]!=-1) pushdown(o,l,r);
35     if (ql<=mid) ans+=query_range(lc,l,mid,ql,qr);
36     if (qr>mid) ans+=query_range(rc,mid+1,r,ql,qr);
37     return ans;
38 }
39
40 void modify(int o,int l,int r,int ql,int qr,int c) { //区级修改
41     if (ql>qr) return;
42     if (ql<=l && qr>=r) {
43       set[o]=c; sum[o]=c*(r-l+1);
44       return;
45     }
46     int mid=l+r>>1;
47     if (set[o]!=-1) pushdown(o,l,r);
48     if (ql<=mid) modify(lc,l,mid,ql,qr,c);
49     if (qr>mid) modify(rc,mid+1,r,ql,qr,c);
50     sum[o]=sum[lc]+sum[rc];
51 }
52
53 int query_single(int o,int l,int r,int p) { //单点查询(其实不用那么麻烦)
54     if (l==r) return sum[o];
55     int mid=l+r>>1;
56     if (set[o]!=-1) pushdown(o,l,r);
57     if (p<=mid) return query_single(lc,l,mid,p); else return query_single(rc,mid+1,r,p);
58 }
59
60 int check(int midd) { //判断
61       for (int i=1;i<=n;++i) b[i]=a[i]>=midd;
62     build(1,1,n);
63       for (int i=1;i<=m;++i) {
64           if (bj[i]==0) {
65               int tmp=query_range(1,1,n,x[i],y[i]); //找1的个数
66               modify(1,1,n,x[i],y[i]-tmp,0); //前一段变为0
67               modify(1,1,n,y[i]-tmp+1,y[i],1); //后一段变为1
68           }
69         else {
70           int tmp=query_range(1,1,n,x[i],y[i]);
71           modify(1,1,n,x[i],x[i]+tmp-1,1); //前一段变为1
72           modify(1,1,n,x[i]+tmp,y[i],0); //后一段变为0
73         }
74       }
75     int tmp=query_single(1,1,n,inx); //查找当前位置的值
76     return tmp==1;
77 }
78
79 int main() {
80     n=read(); m=read();
81       for (int i=1;i<=n;++i) a[i]=read();
82       for (int i=1;i<=m;++i) {
83           bj[i]=read(); x[i]=read(); y[i]=read();
84       }
85     inx=read();
86     int l=1,r=n,mid,res;
87       while (l<=r) {
88           mid=l+r>>1;
89             if (check(mid)) res=mid,l=mid+1;
90               else r=mid-1;
91       }
92     printf("%d",res);
93     return 0;
94 }

其实用合并线段树可以把复杂度降到nlogn,但我不会打。

原文地址:https://www.cnblogs.com/Gaxc/p/9738017.html

时间: 2024-10-08 09:01:24

bzoj4552 [Tjoi2016&Heoi2016]排序 (线段树+二分)的相关文章

bzoj4552 [Tjoi2016&amp;Heoi2016]排序

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第二

[bzoj4552][Tjoi2016][Heoi2016]排序

Description 给出一个到的全排列,现在对这个全排列序列进行次局部排序,排序分为种: 表示将区间的数字升序排序; 表示将区间的数字降序排序. 最后询问第位置上的数字. Input 第行为两个整数和.表示序列的长度,表示局部排序的次数. 第行为个整数,表示到的一个全排列. 接下来输入行,每行有个整数 为代表升序排序,为1代表降序排序;表示排序的区间. 最后输入一个整数,表示排序完之后询问的位置. Output 输出数据仅有一行一个整数,表示按照顺序将全部的部分排序结束后第位置上的数字. S

[BZOJ] 4552: [Tjoi2016&amp;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

4552: [Tjoi2016&amp;Heoi2016]排序

4552: [Tjoi2016&Heoi2016]排序 Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q 位置上的数字. Input 输入数据的第一行为两个整数n和m.n表示序列的长度,

hdu 4893 (多校1007)Wow! Such Sequence!(线段树&amp;二分&amp;思维)

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 352    Accepted Submission(s): 104 Problem Description Recently, Doge got a funny birthday present from his new friend, Prot

hdu 5592 ZYB&#39;s Premutation (线段树+二分查找)

链接: http://acm.hdu.edu.cn/showproblem.php?pid=5592 Problem Description ZYB has a premutation P,but he only remeber the reverse log of each prefix of the premutation,now he ask you to  restore the premutation.Pair (i,j)(i<j) is considered as a reverse

Educational Codeforces Round 64 (Rated for Div. 2) (线段树二分)

题目:http://codeforces.com/contest/1156/problem/E 题意:给你1-n  n个数,然后求有多少个区间[l,r] 满足    a[l]+a[r]=max([l,r]) 思路:首先我们去枚举区间肯定不现实,我们只能取把能用的区间去用,我们可以想下每个数当最大值的时候所做的贡献 我们既然要保证这个数为区间里的最大值,我们就要从两边扩展,找到左右边界能扩展在哪里,这里你直接去枚举肯定不行 这里我们使用了线段树二分去枚举左右区间最远能走到哪里,然后很暴力的去枚举短

codeforces 589G:线段树+二分

离线做 先按照t[]建线段树,维护区间的有效天数以及可用时间 然后把t[]从小到达排序,记得记录t中元素在线段树中的位置 把询问按照d从小到大排序,依次考虑 由于按照d排序,所以当前询问肯定是d最短的,我们在t数组中找到大于当前d的第一个元素位置,把这之前的元素全部从线段树中删除,表示这些天数无效 因为当前d已经是最小,所以删除对后续不会有影响 二分一个天数,查找0~mid天一共的工作时间(工作时间=总可用时间-准备时间*有效天数) #include"cstdio" #include&

bzoj 4552: [Tjoi2016&amp;Heoi2016]排序

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^