[POJ2104]K-th Number

思路1:

归并树+二分答案。

首先构建一棵归并树,然后二分查找最小的数$x$使得被查找区间中小于等于$x$的数的个数大于等于$k$。

注意vector中数字是从$0$开始存的,而询问是从$1$开始的,所以二分的时候要$-1$防止错位。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<vector>
 4 #define maxn 100000
 5 #define root 1
 6 #define _left <<1
 7 #define _right <<1|1
 8 using namespace std;
 9 int n,m,i,j,k;
10 struct SegmentTree {
11     vector<int> val[maxn<<2];
12     void push_up(const int p,const int b,const int e) {
13         val[p].resize(e-b+1);
14         merge(val[p _left].begin(),val[p _left].end(),val[p _right].begin(),val[p _right].end(),val[p].begin());
15     }
16     void build(const int p,const int b,const int e) {
17         if(b==e) {
18             int t;
19             cin>>t;
20             val[p].push_back(t);
21             return;
22         }
23         int mid=(b+e)>>1;
24         build(p _left,b,mid);
25         build(p _right,mid+1,e);
26         push_up(p,b,e);
27     }
28     int query(const int p,const int b,const int e,const int l,const int r,const int x) {
29         if((b==l)&&(e==r)) {
30             return upper_bound(val[p].begin(),val[p].end(),x)-val[p].begin();
31         }
32         else {
33             int mid=(b+e)>>1,ans=0;
34             if(l<=mid) ans+=query(p _left,b,mid,l,min(mid,r),x);
35             if(r>mid) ans+=query(p _right,mid+1,e,max(mid+1,l),r,x);
36             return ans;
37         }
38     }
39     void solve() {
40         int b=1,e=n;
41         while(b<e) {
42             int mid=(b+e)>>1;
43             if(query(root,1,n,i,j,val[root][mid-1])>=k) {
44                 e=mid;
45             }
46             else {
47                 b=mid+1;
48             }
49         }
50         cout<<val[root][e-1]<<endl;
51     }
52 };
53 SegmentTree tree;
54 int main() {
55     ios_base::sync_with_stdio(false);
56     cin>>n>>m;
57     tree.build(root,1,n);
58     while(m--) {
59         cin>>i>>j>>k;
60         tree.solve();
61     }
62     return 0;
63 }
View Code

思路2:

主席树。
建立一颗可持久化权值线段树,每次从$a$中取得一个数字时就将对应的数增加一个权值,并新增一条对应的链,那么每个节点上存储的数即位对应的数的权值前缀和。
查询时只要每次判断左边的的权值和是否$≥k$,如果是,则说明要查询的数在左边,否则在右边,递归查询即可。

 1 #include<cstring>
 2 #include<utility>
 3 #include<iostream>
 4 #include<algorithm>
 5 const int N=100000,M=500001,SIZE=10000000;
 6 class FotileTree {
 7     private:
 8         int val[SIZE],sz,left[SIZE],right[SIZE];
 9         int newnode() {
10             return sz++;
11         }
12     public:
13         int root[M];
14         FotileTree() {
15             sz=0;
16             memset(val,0,sizeof val);
17         }
18         int build(const int b,const int e) {
19             int new_p=newnode();
20             if(b==e) return new_p;
21             int mid=(b+e)>>1;
22             left[new_p]=build(b,mid);
23             right[new_p]=build(mid+1,e);
24             return new_p;
25         }
26         int modify(const int p,const int b,const int e,const int x) {
27             int new_p=newnode();
28             val[new_p]=val[p]+1;
29             if(b==e) return new_p;
30             int mid=(b+e)>>1;
31             if(x<=mid) left[new_p]=modify(left[p],b,mid,x),right[new_p]=right[p];
32             if(x>mid) right[new_p]=modify(right[p],mid+1,e,x),left[new_p]=left[p];
33             return new_p;
34         }
35         int query(const int p1,const int p2,const int b,const int e,const int k) {
36             if(b==e) return b;
37             int mid=(b+e)>>1;
38             if(val[left[p2]]-val[left[p1]]>=k) return query(left[p1],left[p2],b,mid,k);
39             return query(right[p1],right[p2],mid+1,e,k-val[left[p2]]+val[left[p1]]);
40         }
41 };
42 FotileTree t;
43 int main() {
44     std::ios_base::sync_with_stdio(false);
45     std::cin.tie(NULL);
46     int n,m;
47     std::cin>>n>>m;
48     t.root[0]=t.build(1,n);
49     std::pair<int,int> a[n];
50     int hash[n],antihash[n];
51     for(int i=0;i<n;i++) {
52         std::cin>>a[i].first;
53         a[i].second=i;
54     }
55     std::sort(&a[0],&a[n]);
56     for(int i=0;i<n;i++) {
57         hash[a[i].second]=i+1;
58         antihash[i]=a[i].first;
59     }
60     for(int i=1;i<=n;i++) t.root[i]=t.modify(t.root[i-1],1,n,hash[i-1]);
61     while(m--) {
62         int l,r,k;
63         std::cin>>l>>r>>k;
64         std::cout<<antihash[t.query(t.root[l-1],t.root[r],1,n,k)-1]<<std::endl;
65     }
66     return 0;
67 }
View Code

 思路3:

树状数组+整体二分。
同时对询问的区间和答案进行分治,属于同一区间的询问可以同时分治,
用树状数组维护小于等于$mid$的数字个数$cnt$,
如果$cntleq{k}$则往小二分,如果$cnt>k$则往大二分。
以前也分别用归并树和主席树A过此题。
注意每个数的值可能是负数,写读优时要注意。(每次做这题都因为这个原因WA过)

 1 #include<iostream>
 2 #include<algorithm>
 3 const int inf=0x7fffffff;
 4 const int N=200001,M=5001;
 5 struct Modify {
 6     int id,val;
 7     bool operator < (const Modify &x) const {
 8         return val<x.val;
 9     }
10 };
11 Modify a[N];
12 struct Query {
13     int a,b,k,cnt,id;
14 };
15 Query q[M],s[M];
16 int n;
17 class FenwickTree {
18     private:
19         int val[N];
20         int lowbit(const int x) {
21             return x&-x;
22         }
23     public:
24         void modify(int p,const int x) {
25             while(p<=n) {
26                 val[p]+=x;
27                 p+=lowbit(p);
28             }
29         }
30         int query(int p) {
31             int ans=0;
32             while(p) {
33                 ans+=val[p];
34                 p-=lowbit(p);
35             }
36             return ans;
37         }
38 };
39 FenwickTree t;
40 int ans[M];
41 void solve(int b,int e,int l,int r) {
42     if(b==e) {
43         for(int i=l;i<=r;i++) ans[q[i].id]=b;
44         return;
45     }
46     int ll=1,rr=n+1;
47     while(ll<rr) {
48         int mid=(ll+rr)>>1;
49         if(a[mid].val>=b) {
50             rr=mid;
51         }
52         else {
53             ll=mid+1;
54         }
55     }
56     int mid=(b+e)>>1;
57     for(int i=rr;i<=n&&a[i].val<=mid;i++) t.modify(a[i].id,1);
58     for(int i=l;i<=r;i++) q[i].cnt=t.query(q[i].b)-t.query(q[i].a-1);
59     for(int i=rr;i<=n&&a[i].val<=mid;i++) t.modify(a[i].id,-1);
60     int tmpl=l,tmpr=r;
61     for(int i=l;i<=r;i++) {
62         if(q[i].cnt>=q[i].k) {
63             s[tmpl++]=q[i];
64         }
65         else {
66             q[i].k-=q[i].cnt;
67             s[tmpr--]=q[i];
68         }
69     }
70     for(int i=l;i<=r;i++) q[i]=s[i];
71     if(tmpl!=l) solve(b,mid,l,tmpl-1);
72     if(tmpr!=r) solve(mid+1,e,tmpr+1,r);
73 }
74 int main() {
75     std::ios_base::sync_with_stdio(false);
76     std::cin.tie(NULL);
77     int m;
78     std::cin>>n>>m;
79     int min=inf,max=-inf;
80     for(int i=1;i<=n;i++) {
81         a[i].id=i;
82         std::cin>>a[i].val;
83         min=std::min(min,a[i].val);
84         max=std::max(max,a[i].val);
85     }
86     std::sort(&a[1],&a[n+1]);
87     for(int i=1;i<=m;i++) {
88         std::cin>>q[i].a>>q[i].b>>q[i].k;
89         q[i].id=i;
90     }
91     solve(min,max,1,m);
92     for(int i=1;i<=m;i++) {
93         std::cout<<ans[i]<<std::endl;
94     }
95     return 0;
96 }
View Code
原文地址:https://www.cnblogs.com/skylee03/p/6847201.html