[hdu-6621]K-th Closest Distance 主席树 线段树 2019 多校4

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6621

题意:给一个数组,多次询问 l,r,k,p,问 l~r区间 第k小的|p-ai|是多少
1<=p、ai<1e6 、1 <= K <= 169, R - L + 1 >= K,1 <= n, m <= 10^5

题解:二分答案+主席树
二分绝对值答案r,然后用主席树查[p-r , p+r]区间有多少个数,如果大于等于k个减小r并记录答案,小于k个增大r
有一种情况是r满足k个,但是这个r的答案不存在,这时候会二分更小的r,直到精确到存在的那个r
主席树维护 当前节点l~r值范围内的数总个数有几个

官方题解

Using segment tree, we can find the number of values smaller than p in [L, R] within O(log(n)).

So by using binary search method, we can find the answer in O(log(n)^2) time.

Total time complexity is O(N log(N) + Q log(N)^2).

AC代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 int const maxn=1e5+100,maxn2=1e6+10;
 5 int m,n,q,a[maxn],tot,froot[maxn],c[maxn2*30],lson[maxn2*30],rson[maxn2*30];//主席树的空间开n的40倍一般够了
 6 
 7 int update(int root,int pos,int val){//加入一个新的值后新的树,root是前一棵树
 8     int newroot=tot++,tmp=newroot;
 9     c[newroot]=c[root]+val;//新树的结点值=原先树的值+新加的
10     int l=1,r=1e6;
11     while(l<r){
12         int mid=(l+r)>>1;
13         if(pos<=mid){//看修改的值位于哪颗子树上
14             lson[newroot]=tot++;rson[newroot]=rson[root];//如果位于左子树上,则右子树的值可以利用原先的树
15             newroot = lson[newroot];root=lson[root];
16             r=mid;
17         }
18         else {
19             rson[newroot]=tot++;lson[newroot]=lson[root];
20             newroot=rson[newroot];root=rson[root];
21             l=mid+1;
22         }
23         c[newroot]=c[root]+val;
24     }
25     return tmp;//新的树的根节点
26 }
27 int query(int l,int r,int k,int lr,int rr){
28     //当前点l、r时刻树编号编号分别为lr、rr,
29     //当前点控制区间为[l,r],要查询区间为[1,k] 
30     if(k>=r)return c[rr]-c[lr];
31     int mid=(l+r)>>1,ans=0;
32     if(1<=mid)ans+=query(l,mid,k,lson[lr],lson[rr]);
33     if(k>mid)ans+=query(mid+1,r,k,rson[lr],rson[rr]);
34     return ans;
35 }
36 int query_node(int lr,int rr,int p,int k){//用线段树维护个数
37     int l=0,r=1e6,mid,ans,al,ar,gg;//二分绝对值答案的半径    |p-ai|
38     while(l<=r){
39         mid=(l+r)>>1;        
40         al=p-mid,ar=p+mid;//看al——ar区间有多少个数,若<k个,则半径要增大,若<=k个,半径可能对,可能需要减小(因为当前分到的数可能不存在),但答案一定是稍微大了可以小了不行。
41         if(al<=1)gg=query(1,1e6,ar,froot[lr],froot[rr]);//若al<0,就看0~ar有多少个数
42         else gg=query(1,1e6,ar,froot[lr],froot[rr])-query(1,1e6,al-1,froot[lr],froot[rr]);
43         if(gg>=k){
44             ans=mid;r=mid-1;
45         }
46         else l=mid+1;
47     }
48     return ans;
49 }
50 int build(int l,int r){//初始建树
51     int root=tot++;
52     c[root]=0;
53     if(l!=r){
54         int mid=(l+r)>>1;
55         lson[root]=build(l,mid);
56         rson[root]=build(mid+1,r);
57     }
58     return root;
59 }
60 inline int get_num(){
61     char ch;
62     bool flag=false;
63     int num=0;
64     ch=getchar();
65     while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
66     while(ch>='0'&&ch<='9'){num=(num<<3)+(num<<1)+ch-'0';ch=getchar();}
67     if(flag)return -1*num;
68     return num;
69 }
70 int main(){
71         int T,ans;
72         scanf("%d",&T);
73         while(T--){
74         scanf("%d%d",&n,&q);
75         tot=0,ans=0;
76         for(int i=1;i<=n;i++){
77             a[i]=get_num();
78         }
79         
80         froot[0]= build(1,1e6);
81         for(int i=1;i<=n;i++){
82             froot[i]=update(froot[i-1],a[i],1);
83         }
84         while(q--){
85             int l,r,p,k;
86             l=get_num()^ans,r=get_num()^ans,p=get_num()^ans,k=get_num()^ans;
87             ans=query_node(l-1,r,p,k);
88             printf("%d\n",ans);
89         }    
90         }
91         
92     return 0;
93 }

猜你喜欢

转载自www.cnblogs.com/conver/p/11286918.html