【BZOJ2653】middle,主席树(非权值线段树)维护区间01信息+二分答案

传送门

写在前面:虽然这是一道我再也不想写的题目,但很好很有价值

思路:

cxlove大神

要求中位数最大,首先二分中位数,然后判断可行不可行。

判断X可行不可行,对于区间内的数,凡是>=X的标为1,否则为-1。这样的话,求一次最大区间和 如果大于等于0,则说明可行。

这要求我们不能像之前那样建立权值线段树的主席树(区间即为权值)了,而是以权值为下标,维护区间[1,n]的信息,可能有点拗口,这里就理解是我们平常写的普通线段树好了,只是这里是n棵由于根的不同而信息不同的线段树

具体实现

对于题目要求,我们很容易维护[b+1,c-1]间的信息,即求得序列总和,而[a,b]的右起最大子序列和(必须包含右端点)与[c,d]左起最大子序列和(必须包含左端点)确实把我难了好久,唉……

一开始建树的时候每个位置都是1(原本所有位置的标记都是1)。序列按照从小到大顺序插入主席树。做第i棵树的时候,它第i-1大的数的位置上改为-1。什么意思呢,就是说,如果X取的是第i大的那个数,那么第i-1大的数的标记就要改为-1(更小的那些在做第i-1棵树的时候就已经改为-1了)。于是在询问X的时候是计算root[X]这棵线段树里面的各种值了。

最后是如何求[[a,b],[c,d]]区间的最大子段和。其实很简单,因为(b,c)这段是一定要取的,所以答案就是[a,b]的最大右连续和+(b,c)的区间和+[c,d]的最大左连续和。

注意:

离散化后的值,下标什么的好烦人啊!很容易搞混的说

代码:

#include<bits/stdc++.h>
#define M 20004
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
using namespace std;
int n,q,cnt,lastans;
int c[M],ID[M];
struct disc
{
    int data,id;
    bool operator <(const disc other)const
    {
        return data<other.data;
    }
}b[M];
struct Chairman_tree
{
    int sum_L,sum_R,sum,ch[2];
}a[M*16];
void pushup(int rt,int begin,int end)
{
    int mid=(begin+end)>>1;
    a[rt].sum=a[ls(rt)].sum+a[rs(rt)].sum;
    a[rt].sum_L=max(a[ls(rt)].sum_L,a[ls(rt)].sum+a[rs(rt)].sum_L);
    a[rt].sum_R=max(a[rs(rt)].sum_R,a[rs(rt)].sum+a[ls(rt)].sum_R);
}
void build(int rt,int begin,int end)
{
    if (begin==end)
    {
        a[rt]=(Chairman_tree){1,1,1,{0,0}};
        return;
    }
    int mid=(begin+end)>>1;
    ls(rt)=++cnt;
    rs(rt)=++cnt;
    build(ls(rt),begin,mid);
    build(rs(rt),mid+1,end);
    pushup(rt,begin,end);
}
void insert(int now,int L,int R,int rt,int pos,int val)
{
    if (L==R)
    {
        a[rt]=(Chairman_tree){val,val,val,{0,0}};
        return;
    }
    int mid=(L+R)>>1;
    if (mid>=pos)
        rs(rt)=rs(now),
        ls(rt)=++cnt,
        insert(ls(now),L,mid,ls(rt),pos,val);
    else
        ls(rt)=ls(now),
        rs(rt)=++cnt,
        insert(rs(now),mid+1,R,rs(rt),pos,val);
    pushup(rt,L,R);
}
int get_sum(int rt,int begin,int end,int l,int r)
{
    if (l>r) return 0;
    if (l<=begin&&end<=r) return a[rt].sum;
    int mid=(begin+end)>>1,ans=0;
    if (mid>=l) ans+=get_sum(ls(rt),begin,mid,l,r);
    if (mid<r) ans+=get_sum(rs(rt),mid+1,end,l,r);
    return ans;
}
int get_L(int rt,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return a[rt].sum_L;
    int mid=(begin+end)>>1;
    if (mid>=r) return get_L(ls(rt),begin,mid,l,r);
    else if (mid<l) return get_L(rs(rt),mid+1,end,l,r);
    else return max(get_L(ls(rt),begin,mid,l,r),get_sum(ls(rt),begin,mid,l,r)+get_L(rs(rt),mid+1,end,l,r));
}
int get_R(int rt,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return a[rt].sum_R;
    int mid=(begin+end)>>1;
    if (mid>=r) return (0,get_R(ls(rt),begin,mid,l,r));
    else if (mid<l) return get_R(rs(rt),mid+1,end,l,r);
    else return max(get_R(rs(rt),mid+1,end,l,r),get_sum(rs(rt),mid+1,end,l,r)+get_R(ls(rt),begin,mid,l,r));
}
main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",c+i),
        b[i].id=i,b[i].data=c[i];
    sort(b+1,b+n+1);
    for (int i=1;i<=n;i++)
        c[b[i].id]=i,
        ID[i]=b[i].data;
    cnt=n;
    build(1,1,n);
    for (int i=2;i<=n;i++) insert(i-1,1,n,i,b[i-1].id,-1);
    scanf("%d",&q);
    int p[4];
    while (q--)
    {
        for (int i=0;i<4;i++)
            scanf("%d",p+i),
            p[i]=(p[i]+lastans)%n,
            p[i]++;
        sort(p,p+4);
        int l=1,r=n,mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if (get_sum(mid,1,n,p[1]+1,p[2]-1)+get_R(mid,1,n,p[0],p[1])+get_L(mid,1,n,p[2],p[3])>=0)
                l=mid+1,lastans=ID[mid];
            else
                r=mid-1;
        }
        printf("%d\n",lastans);
    }
}
时间: 2024-10-08 14:45:48

【BZOJ2653】middle,主席树(非权值线段树)维护区间01信息+二分答案的相关文章

【bzoj3065】带插入区间K小值 替罪羊树套权值线段树

题目描述 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少.这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问.这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少.这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问.(orz 主席

BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

题目大意:有一些位置,这些位置上可以放若干个数字.现在有两种操作. 1.在区间l到r上添加一个数字x 2.求出l到r上的第k大的数字是什么 思路:这种题一看就是树套树,关键是怎么套,怎么写.(话说我也不会来着..)最容易想到的方法就是区间线段树套一个权值线段树,但是区间线段树上的标记就会变得异常复杂.所以我们就反过来套,用权值线段树套区间线段树.这样修改操作在外线段树上就变成了单点修改,外线段树就不用维护标记了.在里面的区间线段树上维护标记就容易多了.具体实现见代码. CODE: #includ

BZOJ_3685_普通van Emde Boas树_权值线段树

Description 设计数据结构支持: 1 x  若x不存在,插入x 2 x  若x存在,删除x 3    输出当前最小值,若不存在输出-1 4    输出当前最大值,若不存在输出-1 5 x  输出x的前驱,若不存在输出-1 6 x  输出x的后继,若不存在输出-1 7 x  若x存在,输出1,否则输出-1 Input 第一行给出n,m 表示出现数的范围和操作个数 接下来m行给出操作 n<=10^6,m<=2*10^6,0<=x<n Output Sample Input 1

权值线段树&amp;&amp;线段树合并

权值线段树 所谓权值线段树,就是一种维护值而非下标的线段树,我个人倾向于称呼它为值域线段树. 举个栗子:对于一个给定的数组,普通线段树可以维护某个子数组中数的和,而权值线段树可以维护某个区间内数组元素出现的次数. 在实现上,由于值域范围通常较大,权值线段树会采用离散化或动态开点的策略优化空间. 更新操作: 更新的时候,我们向线段树中插入一个值v,那么所有包含v的区间值都需要+1.(每个节点维护对应区间中出现了多少个数) int update (long long v,long long l,lo

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

HDU6621 K-th Closest Distance 第 k 小绝对值(主席树(统计范围的数有多少个)+ 二分 || 权值线段树+二分)

题意:给一个数组,每次给 l ,r, p, k,问区间 [l, r] 的数与 p 作差的绝对值的第 k 小,这个绝对值是多少 分析:首先我们先分析单次查询怎么做: 题目给出的数据与多次查询已经在提示着我们在用数据结构去解决这个问题,对于常见的处理区间的数据结构首选线段树啦: 我觉得这道题的关键在于此:我们需要去二分答案ans,  为什么呢? 我们这样观察 ,对于 | p-a[i] | <= ans  等于 p-ans<=a[i] <=p+ans   那问题就转化为查询[L,R] 区间里面

权值线段树&amp;&amp;可持久化线段树&amp;&amp;主席树

权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出现的次数 2.权值很大怎么办?数组空间不够啊 ----- 可以先离散化,再记录 3.那权值线段树到底是用来干嘛的呢? ----- 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦) 那第k小值该怎么求呢??? 从树根依次往下 若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子 若当前

【BZOJ3110】【codevs1616】K大数查询,权值线段树套普通线段树

Time:2016.05.09 Author:xiaoyimi 转载注明出处谢谢 传送门1 传送门2 思路: 之前没怎么接触过权值线段树(非主席树),这次就当学习了一下吧.一开始还把题意理解错了,我的天啊-- 起初思考了好久,发现不知道怎么处理负数的情况,不过数据里并没有负数? 权值线段树的每个节点表示一个区间[L,R],存储原序列中权值为[L,R]的元素的信息,所以这里的权值线段树每个节点上都是一棵普通线段树,就是负责统计原序列中权值为[L,R]的元素的个数. 每次插入一个相同的值x时就相当于

【bzoj2770】YY的Treap 权值线段树

题目描述 志向远大的YY小朋友在学完快速排序之后决定学习平衡树,左思右想再加上SY的教唆,YY决定学习Treap.友爱教教父SY如砍瓜切菜般教会了YY小朋友Treap(一种平衡树,通过对每个节点随机分配一个priority,同时保证这棵平衡树关于priority是一个小根堆以保证效率).这时候不怎么友爱的510跑了出来,他问了YY小朋友一个极不和谐的问题:怎么求Treap中两个点之间的路径长度.YY秒了之后决定把这个问题交给你来做,但只要求出树中两点的LCA. 输入 第一行两个整数n,m 第二行