【BZOJ4552】排序(线段树,二分答案)

【BZOJ4552】排序(线段树,二分答案)

题面

BZOJ

题解

好神的题啊
直接排序我们做不到
怎么维护?
考虑一下,如果我们随便假设一个答案
怎么检验它是否成立?
把这个数设成\(1\),其他的数字都设成\(0\)
最后检查一下这个位置是不是\(1\)就好啦

但是这样没法排序
那么,我们考虑二分一个答案,
把所有比\(mid\)大的数都设成\(1\)
这样,如果在第\(Q\)位上的数字是\(1\)
意味着有一个不小于当前\(mid\)的数在这个位置上
否则就是一个比\(mid\)小的数在这个位置上
这样就可以二分答案啦

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Work{int l,r,opt;}p[MAX];
int t[MAX<<2],tag[MAX<<2];
int a[MAX],b[MAX],n,m,Q;
void Build(int now,int l,int r)
{
    tag[now]=-1;
    if(l==r){t[now]=b[l];return;}
    int mid=(l+r)>>1;
    Build(lson,l,mid);Build(rson,mid+1,r);
    t[now]=t[lson]+t[rson];
}
void puttag(int now,int l,int r,int w)
{
    tag[now]=w;
    t[now]=(r-l+1)*w;
}
void pushdown(int now,int l,int r)
{
    if(tag[now]==-1)return;
    int mid=(l+r)>>1;
    puttag(lson,l,mid,tag[now]);
    puttag(rson,mid+1,r,tag[now]);
    tag[now]=-1;
}
int Query(int now,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)return t[now];
    pushdown(now,l,r);
    int ret=0,mid=(l+r)>>1;
    if(L<=mid)ret+=Query(lson,l,mid,L,R);
    if(R>mid)ret+=Query(rson,mid+1,r,L,R);
    return ret;
}
void Modify(int now,int l,int r,int L,int R,int w)
{
    if(L<=l&&r<=R){puttag(now,l,r,w);return;}
    pushdown(now,l,r);
    int mid=(l+r)>>1;
    if(L<=mid)Modify(lson,l,mid,L,R,w);
    if(R>mid)Modify(rson,mid+1,r,L,R,w);
    t[now]=t[lson]+t[rson];
}
void Work(int i)
{
    int ss=Query(1,1,n,p[i].l,p[i].r);
    if(p[i].opt==0)
    {
        ss=p[i].r-p[i].l+1-ss;
        if(ss!=0)Modify(1,1,n,p[i].l,p[i].l+ss-1,0);
        if(ss!=p[i].r-p[i].l+1)Modify(1,1,n,p[i].l+ss,p[i].r,1);
    }
    else
    {
        if(ss!=0)Modify(1,1,n,p[i].l,p[i].l+ss-1,1);
        if(ss!=p[i].r-p[i].l+1)Modify(1,1,n,p[i].l+ss,p[i].r,0);
    }
}
bool check(int mid)
{
    for(int i=1;i<=n;++i)if(a[i]>=mid)b[i]=1;else b[i]=0;
    Build(1,1,n);
    for(int i=1;i<=m;++i)Work(i);
    return Query(1,1,n,Q,Q);
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=m;++i)p[i].opt=read(),p[i].l=read(),p[i].r=read();
    Q=read();
    int l=1,r=n,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))l=mid+1,ans=mid;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/8468953.html

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

【BZOJ4552】排序(线段树,二分答案)的相关文章

bzoj[2402] 陶陶的难题II 树链剖分+线段树+二分答案+凸包

将y+q/x+p的值设为x 由于i,j互不干扰,所以我们可以将x,y p,q拉出来分别计算 问题转化为存在x,y,满足y-mid*x+q-mid*p>=0的情况下mid最大 不难发现答案具有单调性,于是二分答案 显然要取出一对(x,y)或(p,q)使y(q)-x(p)*mid尽可能大 以x,y举例,若取点1比取点2更优(x1>x2),则(y1-y2)/(x1-x2)>=mid 用单调栈维护上凸包,存到线段树中 查询时二分凸包 写麻烦了... 1 #include<cstdio>

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]) 思路:首先我们去枚举区间肯定不现实,我们只能取把能用的区间去用,我们可以想下每个数当最大值的时候所做的贡献 我们既然要保证这个数为区间里的最大值,我们就要从两边扩展,找到左右边界能扩展在哪里,这里你直接去枚举肯定不行 这里我们使用了线段树二分去枚举左右区间最远能走到哪里,然后很暴力的去枚举短

bzoj4552 [Tjoi2016&amp;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,这样整

[题解]luogu_P2824_HEOI2016排序(线段树/二分

很难想,首先要二分答案,这样对于所有大于mid的数可以当做1,所有小于mid的可以当做0,这些1或0内部怎么排其实无所谓,然后1全放一边就可以,单调性的话比较好说,因为p点的数要么比mid大要么小,排列答案只会有一个 #include<bits/stdc++.h> #define ls (x<<1) #define rs (x<<1|1) #define mid ((l+r)>>1) using namespace std; const int maxn=1

codeforces 589G:线段树+二分

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

Codeforces 484E Sign on Fence(可持久化线段树+二分)

题目链接:Codeforces 484E Sign on Fence 题目大意:给定给一个序列,每个位置有一个值,表示高度,现在有若干查询,每次查询l,r,w,表示在区间l,r中, 连续最长长度大于w的最大高度为多少. 解题思路:可持久化线段树维护区间合并,前端时间碰到一题可持久化字典树,就去查了一下相关论文,大概知道了是 什么东西. 将高度按照从大到小的顺序排序,然后每次插入一个位置,线段树维护最长连续区间,因为插入是按照从大到小的顺 序,所以每次的线段树中的连续最大长度都是满足高度大于等于当

hdu 4521 小明系列问题——小明序列 线段树+二分

小明系列问题——小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来找去都是自己早已研究过的序列.小明想既然找不到,那就自己来发明一个新的序列问题吧!小明想啊想,终于想出了一个新的序列

【BZOJ-2653】middle 可持久化线段树 + 二分

2653: middle Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1298  Solved: 734[Submit][Status][Discuss] Description 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整. 给你一个长度为n的序列s. 回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数. 其中a<b<c<d. 位置