bzoj3110[ZJOI2013]K大数查询 树套树

12 篇文章 0 订阅
4 篇文章 0 订阅

题目描述

有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

输出格式:
输出每个询问的结果
输入输出样例

输入样例#1:
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
输出样例#1:
1
2
1
说明

【样例说明】

第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1的数有 1 、2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3大的数是 1 。;
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint

分析:
1.总体的思路可以是整体二分,权值线段树套区间线段树,线段树套Splay,线段树套主席树等,我用的是树套树中的权值线段树的做法
2.操作1中abs(c)<=N,因此我们可以用类似主席树的表示方法,在外层建立一颗权值线段树,每个节点表示一个权值区间的数的个数
3.而对于每一个节点,再建立一颗线段树来维护区间。
4.那么1操作就是权值线段树单点修改,区间线段树区间修改;2操作就是权值线段树区间查询,区间线段树区间查询

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
const int maxn=50010;
const int maxm=12000010;
struct node{
    int op;
    int a,b;
    LL c;
}Q[maxn];
int ql,qr;
int n,m;
map<LL,int>mp;
int num[maxn],val[maxn];
int node;
int lc[maxm],rc[maxm];
LL sum[maxm],add[maxm];
int root[maxn<<4];
void modify(int &o,int l,int r){
    if(!o) o=++node;
    int mid=(l+r)>>1;
    if(ql<=l && qr>=r){add[o]++;sum[o]+=(r-l+1);return;}
    if(ql<=mid) modify(lc[o],l,mid);
    if(qr>mid) modify(rc[o],mid+1,r);
    sum[o]=sum[lc[o]]+sum[rc[o]]+add[o]*(r-l+1);
}
LL query(int &o,int l,int r,LL addv){
    if(!o) o=++node;
    int mid=(l+r)>>1;
    if(ql<=l && qr>=r) return sum[o]+addv*(r-l+1);
    LL ret=0;
    if(ql<=mid) ret+=query(lc[o],l,mid,addv+add[o]);
    if(qr>mid) ret+=query(rc[o],mid+1,r,addv+add[o]);
    return ret;
}
int cnt;
int main(){
    scanf("%d%d",&n,&m);
    int L,R,now;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%lld",&Q[i].op,&Q[i].a,&Q[i].b,&Q[i].c);
        if(Q[i].op==1) num[++cnt]=Q[i].c;
    }
    sort(num+1,num+1+cnt);
    int nm=0;
    for(int i=1;i<=m;i++) if(i==1 || num[i]!=num[i-1]) mp[num[i]]=++nm,val[nm]=num[i];
    LL c;
    for(int i=1;i<=m;i++){
        ql=Q[i].a,qr=Q[i].b;
        now=1,L=1,R=n;
        c=Q[i].c;
        if(Q[i].op==1){
            c=mp[c];
            while(L<R){
                modify(root[now],1,n);
                int mid=(L+R)>>1;
                if(c<=mid) R=mid,now=now<<1;
                else L=mid+1,now=now<<1|1;
            }
            modify(root[now],1,n);
        }else{
            do{
                LL t=query(root[now<<1|1],1,n,0);
                int mid=(L+R)>>1;
                if(t>=c) L=mid+1,now=now<<1|1;
                else R=mid,now=now<<1,c-=t;
            }while(L<R);
            printf("%d\n",val[L]);
        }
    }
    return 0;
}

^_^

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值