BZOJ 3110 K大数查询 树套树

题意:链接

方法:树套树(线段树套线段树)

题解:这题好神啊- -自己在做的时候一顿yy也没yy出用两个线段树来搞,其实我想的是类似二逼那道题那样,用线段树维护总区间,treap维护每个节点,不过这样的话,尼玛修改就是暴力有没有?而且查询的时候也是暴力啊有没有?绝壁不是这么做的啊!

上网上找了找题解综合了大家的思想自己也是懂了这题是咋回事了,也是跪了。

好不扯淡了,谈正经的,两个线段树是怎么搞得。

其实第一棵线段树是区间的线段树,而第二棵是维护值域的线段树,具体解析请看代码。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 20000100
using namespace std ;
int lson[N],rson[N];
int sum[N],col[N];
int root[N];
int n,m,size;
int a,b,c;
//这里的sum是针对于值域为l~r,区间为L~R
//本代码的所有l,r都是代表值域,而L,R都是代表区间
void pushup(int rt)
{
    sum[rt]=sum[lson[rt]]+sum[rson[rt]];
}
void pushdown(int rt,int l,int r)
{
    if(!col[rt]||l==r)return;
    if(!lson[rt])lson[rt]=++size;//动态加点
    if(!rson[rt])rson[rt]=++size;//动态加点
    col[lson[rt]]+=col[rt];
    col[rson[rt]]+=col[rt];
    int mid=(l+r)>>1;
    sum[lson[rt]]+=(mid-l+1)*col[rt];
    sum[rson[rt]]+=(r-mid)*col[rt];
    col[rt]=0;
}
void update(int &rt,int l,int r,int L,int R)
{
    if(!rt)rt=++size;
    pushdown(rt,l,r);
    if(L==l&&r==R)
    {
        sum[rt]+=r-l+1;//每一次添加相当于对于区间L~R每个里面都加入了一个值域在l~r内的数,所以个数为这个。
        col[rt]++;//标记
        return;
    }
    int mid=(l+r)>>1;
    if(R<=mid)update(lson[rt],l,mid,L,R);
    else if(L>mid)update(rson[rt],mid+1,r,L,R);
    else
    {
        update(lson[rt],l,mid,L,mid);
        update(rson[rt],mid+1,r,mid+1,R);
    }
    pushup(rt);
}
int query(int rt,int l,int r,int L,int R)//查询并没有什么可说的
{
    if(!rt)return 0;
    pushdown(rt,l,r);
    if(L==l&&r==R)return sum[rt];
    int mid=(l+r)>>1;
    if(R<=mid)return query(lson[rt],l,mid,L,R);
    else if(L>mid)return query(rson[rt],mid+1,r,L,R);
    else return query(lson[rt],l,mid,L,mid)+query(rson[rt],mid+1,r,mid+1,R);
}
int work(int L,int R)//这是对应查询的
{
    int l=1,r=n,rt=1;//对于值域进行二分。最后分到的一定就是K大数。
    while(l!=r)
    {
        int mid=(l+r)>>1;
        int ans=query(root[rt<<1|1],1,n,L,R);
        if(ans<c){c-=ans,r=mid,rt<<=1;}//如果值域右子数的数量不足c,则再到左子树里查,此时c-=ans;
        else{l=mid+1,rt=rt<<1|1;}
    }
    return r;
}
void insert(int L,int R)
{
    int l=1,r=n,rt=1;
    while(l!=r)
    {
        update(root[rt],1,n,L,R);
        int mid=(l+r)>>1;
        if(c<=mid){r=mid,rt<<=1;}
        else{l=mid+1,rt=rt<<1|1;}
    }
    update(root[rt],1,n,L,R);//不要忘- -单点的区间也要更新。
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int jd;
        scanf("%d%d%d%d",&jd,&a,&b,&c);
        if(jd==1)
        {
            insert(a,b);
        }else printf("%d\n",work(a,b));
    }
}
时间: 2024-11-08 21:34:44

BZOJ 3110 K大数查询 树套树的相关文章

bzoj 3110 K大数查询

第一道整体二分,因为只需要知道每个询问区间中比mid大的数有多少个,就可以直接用线段树区间加,区间求和了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define int long long 6 #define N 50005 7 #define ls x*2,l,mid 8 #define rs x*2+1,mid+1,r 9 u

【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. 输入 第一行N,M接下来M行,每行形如1 a b c或2 a b c 输出 输出每个询问的结果 样例输入 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 样例输出 1 2 1 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

【cdq分治】【整体二分】bzoj 3110: [Zjoi2013] HYSBZ - 3110 K大数查询

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3110 题意:有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少.注意是加入一个数,不是让这个数去求和. 题解:虽然是看cdq找到这题,但是感觉这个和平时做的三维偏序不大一样.这题其实是整体二分.就是首先,每次询问的答案应该是1,n之间的,然后

BZOJ Zjoi2013 K大数查询

刚学了整体二分,跟随神犇的步伐走向了这道题...... 神犇:这道题不是二分答案裸题吗? 我:...... 也许是我真的太弱了吧: 不过好歹是A了,讲一讲我的思路: 首先,我们二分出一个答案mid,然后扫一遍当前区间内的询问,如果加入的数x>=mid,那么把这段区间的值都加1:这样就可以求出区间>=mid的数的个数了. 然后,根据这些东西判断一下当前询问该丢到左边还是右边,递归处理就可以了.还有不要忘了询问的是区间第k大,所以对于丢到左边的询问要先把贡献给算进去. 下面贴代码: 1 #incl

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Outpu

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )

BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ----------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std;

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

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

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b