[题解]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=100009;
int n,m,a[maxn],p,L[maxn],R[maxn],ch[maxn];
struct node{
    int sum,tg;
}t[maxn<<2];
inline void upd(int x){
    t[x].sum=t[ls].sum+t[rs].sum;
}
inline void pd(int x,int l,int r){
    if(t[x].tg){
        t[ls].tg=t[x].tg;
        t[rs].tg=t[x].tg;
        if(t[x].tg==1){t[ls].sum=(mid-l+1);t[rs].sum=(r-mid);}
        else t[ls].sum=t[rs].sum=0;
        t[x].tg=0;
    }
}
void build(int x,int l,int r,int md){
    if(l==r){
        t[x].sum=(a[l]>=md);t[x].tg=0;
        return ;
    }
    build(ls,l,mid,md);build(rs,mid+1,r,md);
    upd(x);t[x].tg=0;
}
void change(int x,int l,int r,int L,int R,int val){
    if(L<=l&&r<=R){
        t[x].sum=val*(r-l+1);
        t[x].tg=val?1:-1;
        return;
    }
    pd(x,l,r);
    if(L<=mid)change(ls,l,mid,L,R,val);
    if(R>mid)change(rs,mid+1,r,L,R,val);
    upd(x);
}
int query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R)return t[x].sum;
    pd(x,l,r);
    int ans=0;
    if(L<=mid)ans+=query(ls,l,mid,L,R);
    if(R>mid)ans+=query(rs,mid+1,r,L,R);
    return ans;
}
int queryp(int x,int l,int r,int pos){
    if(l==r)return t[x].sum;
    pd(x,l,r);
    if(pos<=mid)return queryp(ls,l,mid,pos);
    else return queryp(rs,mid+1,r,pos);
}
bool check(int md){
    build(1,1,n,md);
    for(int i=1;i<=m;i++){
        int cnt1=query(1,1,n,L[i],R[i]);
        if(cnt1==0)continue;
        if(ch[i]==0){
            change(1,1,n,R[i]-cnt1+1,R[i],1);
            change(1,1,n,L[i],R[i]-cnt1,0);
        }
        else{
            change(1,1,n,L[i],L[i]+cnt1-1,1);
            change(1,1,n,L[i]+cnt1,R[i],0);
        }
    }
    return queryp(1,1,n,p);
}
int main(){
//    freopen("4.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&ch[i],&L[i],&R[i]);
    scanf("%d",&p);
    int LL=1,RR=n,ans;
    while(LL<=RR){
        int midd=(LL+RR)>>1;
        if(check(midd))ans=midd,LL=midd+1;
        else RR=midd-1;
    }
    printf("%d",ans);
}

原文地址:https://www.cnblogs.com/superminivan/p/11737065.html

时间: 2024-11-09 00:11:03

[题解]luogu_P2824_HEOI2016排序(线段树/二分的相关文章

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

Codeforces Beta Round #75 (Div. 1 Only) B. Queue 线段树+二分

B. Queue Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 codeforces.com/problemset/problem/91/B Description There are n walruses standing in a queue in an airport. They are numbered starting from the queue's tail: the 1-st walrus stands at the end of th

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,这样整

Blog Post Rating CodeForces - 806E (线段树二分)

题目链接 题目大意: 有一个博客, 初始分数为$0$, 有$n$个人, 第$i$个人有一个期望值$a_i$, 如果第$i$个人浏览博客时,博客赞数高于$a_i$博客分数$-1$, 低于$+1$, 相等不变, 对于每个$i$, 求出$[1,i]$的人按任意顺序浏览博客后最大分数. 题解: 首先, 用贪心可以知道所有人按期望升序排列, 最后得分一定最大 由于期望有负数, 博客分数一定是先减后增的, 然后对这两段分类讨论 对于递减的段, 最后分数为递增递减的临界值 假设临界值为$x$, 设比$x$小的

codeforces 589G:线段树+二分

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

hdu 6070 线段树+二分

HDU - 6070 题意:提交了n次题目,每个题目都是最后一次提交的时候AC的,现在求一个区间,这个区间的AC率是最低的(只考虑这个区间内的题目,同样区间内最后交的一遍是AC的),求最低的AC率 思路:AC率=提交次数/题目数目,即区间长度/题目种类,siz[l,r]/(r-l+1)=p(siz[l,r]表示l r区间内的不同题目的个数,p表示AC率), 把式子做一下调整,siz[l,r]+p*l=p(r+1),二分p(0-1)用线段树维护siz[l,r]+l*p ,然后枚举r,每次更新,因为

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

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

HDU 3450 线段树+二分

点击打开链接 题意:给一个数字序列,问你长度大于2的且相邻两个数的差的绝对值不大于d的情况对9901取余 思路:看了根本不会,都没想到是线段树的题目,弱哭~~~,看了大牛们的题解,算是知道怎么回事了,对于当前的数A,那么以它为最后一个元素可以组成的情况是A-d到A+d的和,也可以这样想,A-d的已经组成了m种情况,那么在不影响小于d的情况下,可以直接将A放到A-d组成的左右序列中,那么直接加就可以了,而后更新A可以组成的情况,数据太大还需要离散化,然后二分找一下A-d和A+d的位置,数据可能没有