bzoj3110[ZJOI2013]K大數查詢 樹套樹
阿新 • • 發佈:2019-02-16
題目描述
有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;
}
^_^