hdu 6621 主席树+二分

题意:给一个数组a,每次询问,给定L, R, p, K,求[ L , R ] 中的数与p做差的绝对值的第k小。

思路:对数组a建立主席树(不用离散化),对于每次询问,二分答案,如果 [ L , R ] 区间中的 [ p - mid , p + mid ] 范围内的数大于K,则说明二分的答案偏大,需要缩小区间;如果等于K,也需要缩小,因为要找到精确的值;小于K则需要放大区间。(可以画数轴理解)

代码:

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+10;
 4 const int N=1e6;
 5 struct node{
 6     int val,l,r;
 7 }tree[maxn*22];
 8 int root[N];
 9 int tot;
10 int n,m;
11 void update(int &p,int x,int l,int r,int rt){
12     p=++tot;
13     tree[p]=tree[rt];
14     tree[p].val++;
15     if(l==r) return;
16     int mid=(l+r)>>1;
17     if(x<=mid) update(tree[p].l,x,l,mid,tree[rt].l);
18     if(x>mid) update(tree[p].r,x,mid+1,r,tree[rt].r);
19 }
20 int query(int u,int v,int x,int y,int l,int r){
21     if(x<=l&&r<=y){
22         return tree[v].val-tree[u].val;
23     }
24     int ans=0;
25     int mid=(l+r)>>1;
26     if(x<=mid) ans+=query(tree[u].l,tree[v].l,x,y,l,mid);
27     if(y>mid) ans+=query(tree[u].r,tree[v].r,x,y,mid+1,r);
28     return ans;
29 }
30 int solve(int L,int R,int x,int y){
31     x=max(x,1);
32     y=min(y,N);
33     return query(root[L-1],root[R],x,y,1,N);
34 }
35 int main()
36 {
37     int T;
38     scanf("%d",&T);
39     while(T--){
40         tot=0;
41         scanf("%d%d",&n,&m);
42         for(int i=1;i<=n;i++){
43             int x;
44             scanf("%d",&x);
45             update(root[i],x,1,N,root[i-1]); 
46         }
47         int ans=0;
48         for(int i=1;i<=m;i++){
49             int L,R,p,K;
50             scanf("%d%d%d%d",&L,&R,&p,&K);
51             L^=ans,R^=ans,p^=ans,K^=ans;
52             int l=0,r=N,mid;
53             while(r>=l){
54                 mid=(l+r)>>1;
55                 if(solve(L,R,p-mid,p+mid)>=K)
56                     r=mid-1;
57                 else l=mid+1;
58             }
59             ans=l;
60             printf("%d\n",ans);
61         }
62     }
63     return 0;
64 }

 

posted @ 2020-08-13 10:50  --HY--  阅读(173)  评论(0编辑  收藏  举报