1. 程式人生 > >bzoj3110[ZJOI2013]K大數查詢 樹套樹

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

輸出格式:
輸出每個詢問的結果
輸入輸出樣例

輸入樣例#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; }

^_^