Topic description
Black Box is a primitive database. It can store an array of integers and a special variable i. Initially the Black Box is empty. and i is equal to 0. This Black Box has to process a sequence of commands.
There are only two commands:
ADD(x): put the x element into the BlackBox;
GET: add 1 to i, then output the i-th smallest number in Blackhox.
Remember: the i-th smallest number is the i-th element of the numbers in the Black Box sorted in ascending order. E.g:
Let's demonstrate a command string with 11 commands. (As shown below)
Now the requirement is to find out the best way to handle a given command string. ADD and GET commands each have a maximum of 200,000. Now use two integer arrays to represent the command string:
1.A(1), A(2),...A(M): A string of elements that will be put into the Black Box. Each number is an integer whose absolute value does not exceed 2000000000, M$200000. For example, the above example is A=(3, 1, a 4, 2, 8, -1000, 2).
2.u(1), u(2),...u(N): Indicates that a GET command appears after the u(j)th element is put into the Black Box. For example, u=(l, 2, 6, 6) in the above example. Enter data without making mistakes.
Input and output format
Input format:The first line, two integers, M, N.
The second line, M integers, representing A(l)
……A(M)。
The third line, N integers, representing u(l)
…u(N)。
Output format:Output the output string obtained by Black Box according to the command string, one number per line.
Input and output example
7 4
3 1 -4 2 8 -1000 2
1 2 6 6
3
3
1
2
illustrate
For 30% of the data, M≤10000;
For 50% of the data, M≤100000:
For 100% of the data, M≤200000.
Solution:
This question is a question to the top pile, but it is still a wave of weighted line segment trees of Amway. . .
权值线段树其实很简单,故名思义它的每个叶子节点都有权值(即每个叶子节点维护的是某个确定的数),它可以很轻松的求出整个区间中的第$k$小的数(注意是整个区间,不是一段区间,权值线段树其实就是弱化的主席树,或者说主席树就是多棵权值线段树)。
拿本题做例题。
首先,我们对原数组离散化(因为数太大,空间开不下),这样离散后最多有$200000$个数,就能建树用叶子节点维护每个数了。
再讲讲插入操作,其实就是单点修改,对于离散后的数每次找到它所在的叶子节点,使其权值$+1$(说明这个数出现了一次),并维护一段区间的数出现的个数(即区间求和)。
然后询问第$k$小的数时,从整个区间$[1,n]$开始递归,当左儿子区间$[l,mid]$中出现的数个数大于等于$k$时,查询左儿子,否则查询右儿子中的第$k-sum[l,mid]$小的数(即左儿子中已有$sum[l,mid]<k$个数,那么整个区间第$k$小的数在右儿子里,且是右儿子中的第$k-sum[l,mid]$小的数),当查询到$l==r$时返回$l$就$ok$了。
代码:
1 #include<bits/stdc++.h> 2 #define il inline 3 #define ll long long 4 #define lson l,m,rt<<1 5 #define rson m+1,r,rt<<1|1 6 using namespace std; 7 const int N=2e5+7; 8 int tr[N<<2],n,m,u[N],s[N],cnt=1; 9 struct numm{ 10 ll v;int id; 11 bool operator < (numm a){return v<a.v;} 12 }a[N]; 13 il ll gi(){ 14 ll a=0;char x=getchar();bool f=0; 15 while((x>'9'||x<'0')&&x!='-')x=getchar(); 16 if(x=='-')x=getchar(),f=1; 17 while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); 18 return f?-a:a; 19 } 20 il void pushup(int rt){tr[rt]=tr[rt<<1]+tr[rt<<1|1];} 21 il void update(int u,int l,int r,int rt){ 22 if(l==u&&r==u){tr[rt]++;return;} 23 tr[rt]++; 24 int m=l+r>>1; 25 if(u<=m)update(u,lson); 26 else update(u,rson); 27 pushup(rt); 28 } 29 il int query(int k,int l,int r,int rt){ 30 if(l==r)return l; 31 int m=l+r>>1; 32 if(tr[rt<<1]>=k)return query(k,lson); 33 else return query(k-tr[rt<<1],rson); 34 } 35 int main() 36 { 37 n=gi(),m=gi(); 38 for(int i=1;i<=n;i++)a[i].v=gi(),a[i].id=i; 39 for(int i=1;i<=m;i++)u[i]=gi(); 40 sort(a+1,a+n+1); 41 for(int i=1;i<=n;i++)s[a[i].id]=i; 42 for(int i=1;i<=n;i++){ 43 update(s[i],1,n,1); 44 while(i==u[cnt]){ 45 printf("%lld\n",a[query(cnt,1,n,1)].v); 46 ++cnt; 47 } 48 } 49 return 0; 50 }