bzoj3110 [Zjoi2013]K大数查询

http://www.elijahqi.win/2018/03/01/bzoj3110/
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

Output

输出每个询问的结果

Sample Input

2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output

1
2
1
HINT

【样例说明】

第一个操作 后位置 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

正解:整体二分?树套树也可以搞 大概我一开始的思路是线段树套splay 应该是正确的 但是时间卡的有点紧 log^3不一定可过 稍微看了眼题解 线段树+线段树 然后思考 发现可做

外层权值线段树 里层 位置线段树 权值线段树就相当于我在二分答案一样 每个管辖的权值都建位置线段树 然后每次在权值线段树上二分即可 注意位置线段树动态开点 保证内存 另外可以像我这样pushdown update也可以像icefox巨佬一样直接类似扫描线覆盖的这么写也Okay 并且常数小很多orz 此题注意longlong 另外题目中说c可能负数 ?真实数据似乎并没有

#include<cstdio>
#include<algorithm>
#define N 55000
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct node{
    int op,a,b,c;
}qr[N];
int n,m,num,cnt,a1[N],nn,rt[N*20],root;
struct node1{
    int left,right;ll s;int lazy;
}tree[N<<1],tr[N*400];
inline void build(int &x,int l,int r){
    x=++num;if (l==r) return;int mid=l+r>>1;
    build(tree[x].left,l,mid);build(tree[x].right,mid+1,r);
}
inline void pushdown(int x,int l,int r){
    if (!tr[x].lazy) return;int mid=l+r>>1;
    if (!tr[x].left) tr[x].left=++cnt;
    if (!tr[x].right) tr[x].right=++cnt;
    int lc=tr[x].left,rc=tr[x].right;
    tr[lc].lazy+=tr[x].lazy;tr[rc].lazy+=tr[x].lazy;
    tr[lc].s+=(ll)tr[x].lazy*(mid-l+1);
    tr[rc].s+=(ll)tr[x].lazy*(r-mid);
    tr[x].lazy=0;
}
inline void ins(int &x,int l,int r,int l1,int r1){
    if (!x) x=++cnt;if(l1<=l&&r1>=r) {++tr[x].lazy;tr[x].s+=r-l+1;return;}
    int mid=l+r>>1;pushdown(x,l,r);if (l1<=mid) ins(tr[x].left,l,mid,l1,r1);
    if (r1>mid) ins(tr[x].right,mid+1,r,l1,r1);
    tr[x].s=tr[tr[x].left].s+tr[tr[x].right].s;
}
inline void insert1(int x,int l,int r,int p,int l1,int r1){
    ins(rt[x],1,n,l1,r1);if (l==r) return;int mid=l+r>>1;
    if (p<=mid) insert1(tree[x].left,l,mid,p,l1,r1);else insert1(tree[x].right,mid+1,r,p,l1,r1);
}
inline ll query(int &x,int l,int r,int l1,int r1){
    if (!x) return 0;int mid=l+r>>1;ll tmp=0;
    if (l1<=l&&r1>=r) return tr[x].s;pushdown(x,l,r);
    if (l1<=mid) tmp+=query(tr[x].left,l,mid,l1,r1);
    if (r1>mid) tmp+=query(tr[x].right,mid+1,r,l1,r1);return tmp;
}
inline int qk(int x,int l,int r,int k,int l1,int r1){
    if (l==r) return l;int mid=l+r>>1;ll tmp=query(rt[tree[x].right],1,n,l1,r1);
    if (k<=tmp) return qk(tree[x].right,mid+1,r,k,l1,r1);
    return qk(tree[x].left,l,mid,k-tmp,l1,r1);
}
int main(){
    freopen("bzoj3110.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=m;++i){
        qr[i].op=read();qr[i].a=read();qr[i].b=read();qr[i].c=read();
        if (qr[i].op==1) a1[++nn]=qr[i].c;
    }sort(a1+1,a1+nn+1);nn=unique(a1+1,a1+nn+1)-a1-1;build(root,1,nn);
    for (int i=1;i<=m;++i){
        if (qr[i].op==1){
            qr[i].c=lower_bound(a1+1,a1+nn+1,qr[i].c)-a1;
            insert1(root,1,nn,qr[i].c,qr[i].a,qr[i].b);
        }
        if (qr[i].op==2) printf("%d\n",a1[qk(root,1,nn,qr[i].c,qr[i].a,qr[i].b)]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值