BZOJ 3110, K大数查询

14 篇文章 0 订阅
3 篇文章 0 订阅

Problem

传送门

Mean

要求维护一个数列,支持在某部分的每个位置填上一个数以及查询某部分第K大的数字。

Analysis

终于学习了一下树套树的姿势……其实和自己想的也差不多……但是不学还是打不出代码来……
外层是权值线段树,内层是区间线段树。
要注意的细节比较多。二分查询过程中结果可能超过 Maxlongint ,需要用 unsigned int 类型传递;权值可以翻转后整体右移等等。
但最主要的还是实现树套树啦~~~

Code

#include<cstdio>
const int N=50000,L=100001,M=16000005;
int n,m,cmd,a,b,c,cnt,root[M],ls[M],rs[M];
unsigned int sum[M],tag[M];
void read(int &x){
    char c;
    bool p=0;
    while((c=getchar())<'0' || c>'9') if(c=='-') p=1;
    x=c-'0';
    while((c=getchar())>='0' && c<='9') x=x*10+c-'0';
    if(p) x=-x;
}
void down(int o,int l,int r){
    if(!ls[o]) ls[o]=++cnt;
    if(!rs[o]) rs[o]=++cnt;
    int mid=l+r>>1;
    tag[ls[o]]+=tag[o],tag[rs[o]]+=tag[o];
    sum[ls[o]]+=(mid-l+1)*tag[o],sum[rs[o]]+=(r-mid)*tag[o];
    tag[o]=0;
}
void modify(int &o,int l,int r,int ql,int qr){
    if(!o) o=++cnt;
    if(tag[o] && l<r) down(o,l,r);
    if(ql==l && r==qr){
        sum[o]+=r-l+1;
        tag[o]++;
        return;
    }
    int mid=l+r>>1;
    if(qr<=mid) modify(ls[o],l,mid,ql,qr);
    else if(ql>mid) modify(rs[o],mid+1,r,ql,qr);
    else{
        modify(ls[o],l,mid,ql,mid);
        modify(rs[o],mid+1,r,mid+1,qr);
    }
    sum[o]=sum[ls[o]]+sum[rs[o]];
}
void insert(int o,int l,int r,int a,int b,int q){
    while(l<r){
        int mid=l+r>>1;
        modify(root[o],1,n,a,b);
        if(q<=mid) r=mid,o<<=1;
        else l=mid+1,o=o<<1|1;
    }
    modify(root[o],1,n,a,b);
}
unsigned int find(int o,int l,int r,int ql,int qr){
    if(!o) return 0;
    if(l==ql && r==qr) return sum[o];
    if(tag[o]) down(o,l,r);
    int mid=l+r>>1;
    if(qr<=mid) return find(ls[o],l,mid,ql,qr);
    if(ql>mid) return find(rs[o],mid+1,r,ql,qr);
    return find(ls[o],l,mid,ql,mid)+find(rs[o],mid+1,r,mid+1,qr);
}
int query(int o,int l,int r,int a,int b,unsigned int c){
    while(l<r){
        int mid=l+r>>1;
        unsigned int t=find(root[o<<1],1,n,a,b);
        if(t<c) l=mid+1,c-=t,o=o<<1|1;
        else r=mid,o<<=1;
    }
    return l;
}
int main(){
    read(n),read(m);
    while(m--){
        read(cmd),read(a),read(b),read(c);
        if(cmd==1) insert(1,1,L,a,b,N-c+1);
        else printf("%d\n",N+1-query(1,1,L,a,b,c));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值