UESTC #1919 一棵复杂的线段树

Description

给一个\(1 \sim n\)的排列,进行\(m\)次操作,可以将一个区间\([l,r]\)内的数升序排序或者降序排序,最后进行一次询问问第\(k\)个数字为多少。

Solution

二分答案,对于每一个二分的值\(x\),将原排列中小于等于\(x\)的数视为\(0\),大于\(x\)的树视为\(1\),用线段树维护,排序操作可以用线段树的区间赋值实现。

Code

/*
 Author: LargeDumpling
 Email: [email protected]
 Edit History:
    2018-07-24  File created.
*/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=100050;
struct jz
{
    int l,r,typ;
}O[MAXN];
int num[MAXN],sum[MAXN<<2],tag[MAXN<<2],L[MAXN<<2],R[MAXN<<2],n,m,k;
void maintain(int root)
{
    sum[root]=sum[root<<1]+sum[root<<1|1];
    return;
}
void build(int root,int l,int r,int x)
{
    L[root]=l; R[root]=r; tag[root]=-1;
    if(l==r)
    {
        sum[root]=(num[l]<=x)?0:1;
        return;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid,x);
    build(root<<1|1,mid+1,r,x);
    maintain(root);
    return;
}
void down(int root)
{
    if(tag[root]==-1) return;
    sum[root<<1]=tag[root]*(R[root<<1]-L[root<<1]+1);
    tag[root<<1]=tag[root];
    sum[root<<1|1]=tag[root]*(R[root<<1|1]-L[root<<1|1]+1);
    tag[root<<1|1]=tag[root];
    tag[root]=-1;
    return;
}
void change(int root,int l,int r,int x)
{
    if(r<l) return; //because of line 98, 99, 103, 104, ignore this will cause a Wrong Answer.
    if(l<=L[root]&&R[root]<=r)
    {
        sum[root]=x*(R[root]-L[root]+1);
        tag[root]=x;
        return;
    }
    down(root);
    int mid=(L[root]+R[root])>>1;
    if(l<=mid) change(root<<1,l,r,x);
    if(mid<r) change(root<<1|1,l,r,x);
    /*if(l<=R[root<<1]) change(root<<1,l,r,x);
    if(R[root<<1]<r) change(root<<1|1,l,r,x);*/ //this will cause a Runtime Error.
    maintain(root);
    return;
}
int query(int root,int l,int r)
{
    if(r<l) return 0;
    if(l<=L[root]&&R[root]<=r) return sum[root];
    down(root);
    int ans=0,mid=(L[root]+R[root])>>1;
    if(l<=mid) ans+=query(root<<1,l,r);
    if(mid<r) ans+=query(root<<1|1,l,r);
    /*if(l<=R[root<<1]) ans+=query(root<<1,l,r);
    if(R[root<<1]<r) ans+=query(root<<1|1,l,r);*/
    return ans;
}
void read1n(int &x)
{
    char ch;
    for(ch=getchar();ch<‘0‘||‘9‘<ch;ch=getchar());
    for(x=0;‘0‘<=ch&&ch<=‘9‘;ch=getchar())
        x=(x<<1)+(x<<3)+ch-‘0‘;
    return;
}
bool check(int x)
{
    int cnt0,cnt1;
    build(1,1,n,x);
    for(int i=1;i<=m;i++)
    {
        cnt1=query(1,O[i].l,O[i].r);
        cnt0=O[i].r-O[i].l+1-cnt1;
        if(O[i].typ)
        {
            change(1,O[i].l,O[i].l+cnt1-1,1);
            change(1,O[i].r-cnt0+1,O[i].r,0);
        }
        else
        {
            change(1,O[i].l,O[i].l+cnt0-1,0);
            change(1,O[i].r-cnt1+1,O[i].r,1);
        }
    }
    return query(1,k,k)==0;
}
int main()
{
    int l,r,mid;
    read1n(n); read1n(k);
    for(int i=1;i<=n;i++)
        read1n(num[i]);
    read1n(m);
    for(int i=1;i<=m;i++)
    {
        read1n(O[i].typ);
        read1n(O[i].l);
        read1n(O[i].r);
    }
    l=0; r=n;
    while(l<r-1)
    {
        mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%d\n",r);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

Other thing

傅大爷数据出的太好了,把我原来线段树的写法卡疯了。

原文地址:https://www.cnblogs.com/LargeDumpling/p/9362321.html

时间: 2024-11-04 13:46:56

UESTC #1919 一棵复杂的线段树的相关文章

UESTC 1073 秋实大哥与线段树(线段树---省时的建树)

题目链接:http://acm.uestc.edu.cn/#/problem/show/1073 “学习本无底,前进莫徬徨.” 秋实大哥对一旁玩手机的学弟说道. 秋实大哥是一个爱学习的人,今天他刚刚学习了线段树这个数据结构. 为了检验自己的掌握程度,秋实大哥给自己出了一个题,同时邀请大家一起来作. 秋实大哥的题目要求你维护一个序列,支持两种操作:一种是修改某一个元素的值:一种是询问一段区间的和. Input 第一行包含一个整数n ,表示序列的长度. 接下来一行包含n  个整数a i  ,表示序列

uestc 1073 秋实大哥与线段树 Label:线段树

秋实大哥与线段树 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) “学习本无底,前进莫徬徨.” 秋实大哥对一旁玩手机的学弟说道. 秋实大哥是一个爱学习的人,今天他刚刚学习了线段树这个数据结构. 为了检验自己的掌握程度,秋实大哥给自己出了一个题,同时邀请大家一起来作. 秋实大哥的题目要求你维护一个序列,支持两种操作:一种是修改某一个元素的值:一种是询问一段区间的和. Input

UESTC 764 失落的圣诞节 --RMQ/线段树

题意:n种物品,每种物品对不同的人都有不同的价值,有三个人选,第一个为普通学生,第二个是集,第三个是祈,集和祈可以选一样的,并且还会获得加分,集和祈选的普通学生都不能选,问三个人怎样选才能使总分最高. 解法: 先把集和祈选一样的和存到一个数组sum,然后可以枚举普通学生选的是哪个,再在sum的左边和右边找一个最大值,更新Maxi,然后再考虑集祈选的不同的情况,即在集的数组两边取个最大值,以及在祈的数组两边取个最大值,相加即可,如果集的最大值和祈的最大值为一个标记时,我们在前面的sum最大值就已经

UESTC 1073 秋实大哥与线段树 (线段树)

“学习本无底,前进莫徬徨.” 秋实大哥对一旁玩手机的学弟说道. 秋实大哥是一个爱学习的人,今天他刚刚学习了线段树这个数据结构. 为了检验自己的掌握程度,秋实大哥给自己出了一个题,同时邀请大家一起来作. 秋实大哥的题目要求你维护一个序列,支持两种操作:一种是修改某一个元素的值:一种是询问一段区间的和. Input 第一行包含一个整数nn,表示序列的长度. 接下来一行包含nn个整数aiai,表示序列初始的元素. 接下来一行包含一个整数mm,表示操作数. 接下来mm行,每行是以下两种操作之一: 1 x

线段树(二)

转自:http://blog.csdn.net/liujian20150808/article/details/51137749 1.线段树的定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 举例描述: 因此有了以上对线段树的定

【bzoj3956】Count 单调栈+可持久化线段树

题目描述 输入 输出 样例输入 3 2 0 2 1 2 1 1 1 3 样例输出 0 3 题解 单调栈+可持久化线段树 本题是 bzoj4826 的弱化版(我为什么做题总喜欢先挑难的做QAQ) $k$对点对$(i,j)$有贡献,当且仅当$a_k=max(a_{i+1},a_{i+2},...,a_{r-1})$,且$a_k<a_i\&\&a_k<a_j$. 那么我们可以使用单调栈求出i左面第一个比它大的位置$lp[i]$,和右面第一个比它大的位置$rp[i]$,那么点对$(lp

线段树之入门篇

线段树(interval tree) 是把区间逐次二分得到的一树状结构,它反映了包括归并排序在内的很多分治算法的问题求解方式. 上图是一棵典型的线段树,它对区间[1,10]进行分割,直到单个点.这棵树的特点 是: 1. 每一层都是区间[a, b]的一个划分,记 L = b - a 2. 一共有log2L层 3. 给定一个点p,从根到叶子p上的所有区间都包含点p,且其他区间都不包含点p. 4. 给定一个区间[l; r],可以把它分解为不超过2log2 L条不相交线段的并. 其中第四点并不是很显然,

笔试算法题(42):线段树(区间树,Interval Tree)

议题:线段树(Interval Tree) 分析: 线段树是一种二叉搜索树,将一个大区间划分成单元区间,每个单元区间对应一个叶子节点:内部节点对应部分区间,如对于一个内部节点[a, b]而言,其左子节点表示的区间为[a, (a+b)/2],其右子节点表示的区间为[1+(a+b)/2, b]: 对于区间长度为N的线段树,由于其单元节点都是[a, a]的叶子节点,所以其叶子节点数为N,并且整棵树为平衡二叉树,所以总节点数为2N-1,树的深度为log(N)+1: 插入操作:将一条线段[a, b]插入到

《数据结构》线段树入门(二)

今天继续介绍——线段树之延迟标记 接上期<数据结构>线段树入门(一):http://www.cnblogs.com/shadowland/p/5870339.html 在上期介绍了线段树的最基本内容(线段树单点修改,区间查询),这次将介绍:区间修改,区间查询. Question: 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述: 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,每行表示操作的个数,如果第一数是1,后接3个正