[bzoj 3110][zjoi 2013]K大数查询

传送门

Description

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

Solution

这题是可以区间线段树套权值线段树来做的

但是我想练一下整体二分

顺便写一个树状数组的区间修改+区间查询

class BIT
{
    #define NM 50005
    #define lb(x) (x&(-x))
    private:
        ll t1[NM],t2[NM],N;
        BIT(){}
    public:
        BIT(int _n):N(_n){memset(t1,0,sizeof t1);memset(t2,0,sizeof t2);}
        inline void CC(int p,int v){for(reg int x=p;x<=N;x+=lb(x))t1[x]+=v,t2[x]+=v*p*1ll;}
        inline void C(int l,int r,int x){CC(l,x);CC(r+1,-x);}
        inline ll GG(int p){ll r=0;for(reg int x=p;x;x-=lb(x))r+=(p+1)*t1[x]-t2[x];return r;}
        inline ll G(int l,int r){return GG(r)-GG(l-1);}
    #undef NM
    #undef lb
};

Code?

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define reg register
class BIT
{
    #define NM 50005
    #define lb(x) (x&(-x))
    private:
        ll t1[NM],t2[NM],N;
        BIT(){}
    public:
        BIT(int _n):N(_n){memset(t1,0,sizeof t1);memset(t2,0,sizeof t2);}
        inline void CC(int p,int v){for(reg int x=p;x<=N;x+=lb(x))t1[x]+=v,t2[x]+=v*p*1ll;}
        inline void C(int l,int r,int x){CC(l,x);CC(r+1,-x);}
        inline ll GG(int p){ll r=0;for(reg int x=p;x;x-=lb(x))r+=(p+1)*t1[x]-t2[x];return r;}
        inline ll G(int l,int r){return GG(r)-GG(l-1);}
    #undef NM
    #undef lb
};
#define MN 50005
struct ques{int l,r,id,opt;ll c;}q[MN],b1[MN],b2[MN];
int n,m,tot,cnt,num[MN],Ans[MN];
void solve(int l=1,int r=tot,int ql=1,int qr=m)
{
    if(ql>qr) return;
//  printf("%d %d %d %d\n",l,r,ql,qr);
    static BIT T(n);register int i;
    if(l==r)
    {
        for(i=ql;i<=qr;++i)if(q[i].opt==2)Ans[q[i].id]=num[l];
        return;
    }
    register int mid=(l+r+1)>>1,tpb1=0,tpb2=0;register ll tmp;
    for(i=ql;i<=qr;++i)
    {
        if(q[i].opt==1)
        {
            if(q[i].c>=num[mid]) T.C(q[i].l,q[i].r,1),b2[++tpb2]=q[i];
            else b1[++tpb1]=q[i];
        }
        else
        {
            tmp=T.G(q[i].l,q[i].r);
            if(tmp<q[i].c) q[i].c-=tmp,b1[++tpb1]=q[i];
            else b2[++tpb2]=q[i];
        }
    }
    for(i=ql;i<=qr;++i)if(q[i].c>=num[mid]&&q[i].opt==1) T.C(q[i].l,q[i].r,-1);
    bool has1=false,has2=false;
    for(i=1;i<=tpb1;++i) q[i+ql-1]=b1[i];
    for(i=1;i<=tpb2;++i) q[qr-tpb2+i]=b2[i];
    solve(l,mid-1,ql,ql+tpb1-1);solve(mid,r,qr-tpb2+1,qr);
}
int main()
{
//  freopen("testdata.in","r",stdin);
//  freopen("testdata.out","w",stdout);
    n=read();m=read();
    register int i;
    for(i=1;i<=m;++i)
    {
        q[i].opt=read(),q[i].l=read(),q[i].r=read(),q[i].c=read();
        if(q[i].opt==1) num[++tot]=q[i].c;
        if(q[i].opt==2) q[i].id=++cnt;
    }
    std::sort(num+1,num+tot+1);
    tot=std::unique(num+1,num+tot+1)-num-1;
    solve();
    for(i=1;i<=cnt;++i) printf("%d\n",Ans[i]);
    return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10172360.html

时间: 2025-01-02 06:32:59

[bzoj 3110][zjoi 2013]K大数查询的相关文章

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

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

BZOJ 3110:[Zjoi2013]K大数查询(整体二分)

http://www.lydsy.com/JudgeOnline/problem.php?id=3110 题意:-- 思路:其实和之前POJ那道题差不多,只不过是换成区间更新,而且是第k大不是第k小,第k大是降序的第k个,在二分询问的时候需要注意和第k小的不同细节. 树状数组比线段树快了几倍,所以说树状数组区间更新区间查询是一个值得学的姿势啊. 线段树: 1 //9028 kb 7484 ms 2 #include <cstdio> 3 #include <cstring> 4 #

数据结构(树套树):ZJOI 2013 K大数查询

有几个点卡常数…… 发现若第一维为位置,第二维为大小,那么修改时第一维修改区间,查询时第一维查询区间,必须挂标记.而这种情况下标记很抽象,而且Push_down不是O(1)的,并不可行. 那要怎么做呢?不妨交换一下,第一维为大小,第二维为位置,在第二维中挂标记,这样Push_down就是O(1)的了. 做完这道题,我最大的启发就是:树套树不适于在第一维挂标记,因为标记的维度会是一维的,根本不好维护. 1 #include <iostream> 2 #include <cstring>

[ZJOI 2013] K大数查询

[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3110 [算法] 整体二分 + 线段树 时间复杂度 : O(NlogN ^ 2) [代码] #include<bits/stdc++.h> using namespace std; #define MAXN 500010 typedef long long ll; typedef long double ld; struct query { int type , a , b; ll

树套树专题——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大数查询( 树状数组套主席树 )

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

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

K大数查询 bzoj 3110

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 [样例说明] 第一个操作 后位置

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