HDU 2665.Kth number-可持久化线段树(无修改区间第K小)模板 (POJ 2104.K-th Number 、洛谷 P3834 【模板】可持久化线段树 1(主席树)只是输入格式不一样,其他几乎都一样的)
Kth number
Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 16941 Accepted Submission(s): 5190
Problem Description
Give you a sequence and ask you the kth big number of a inteval.
Input
The first line is the number of the test cases.
For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere.
The second line contains n integers, describe the sequence.
Each of following m lines contains three integers s, t, k.
[s, t] indicates the interval and k indicates the kth big number in interval [s, t]
For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere.
The second line contains n integers, describe the sequence.
Each of following m lines contains three integers s, t, k.
[s, t] indicates the interval and k indicates the kth big number in interval [s, t]
Output
For each test case, output m lines. Each line contains the kth big number.
Sample Input
1
10 1
1 4 2 3 5 6 7 8 9 0
1 3 2
Sample Output
2
Source
题意就是查询区间第K小,可持久化线段树就可以上场了。
没学可持久化数据结构之前,感觉这个东西是个很高大上,很厉害的东西,等到了解之后发现,就是因为问题的产生所以在原有线段树的基础上有所更新,支持查询历史版本。
如果每更新一次就新建一整棵线段树简直是丧心病狂,所以直接新开节点保存更新的那一条链就可以。
这种东西还是需要自己慢慢体会的,加油,咸鱼。
代码:
1 //无修改区间第K小-可持久化线段树(权值线段树+可持久化) 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<bitset> 7 #include<cassert> 8 #include<cctype> 9 #include<cmath> 10 #include<cstdlib> 11 #include<ctime> 12 #include<deque> 13 #include<iomanip> 14 #include<list> 15 #include<map> 16 #include<queue> 17 #include<set> 18 #include<stack> 19 #include<vector> 20 using namespace std; 21 typedef long long ll; 22 typedef pair<int,int> pii; 23 24 const double PI=acos(-1.0); 25 const double eps=1e-6; 26 const ll mod=1e9+7; 27 const int inf=0x3f3f3f3f; 28 const int maxn=1e5+10; 29 const int maxm=100+10; 30 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 31 #define lson l,m 32 #define rson m+1,r 33 34 int a[maxn],b[maxn],sum[maxn<<5],L[maxn<<5],R[maxn<<5];//sum线段树里保存的值,L左儿子,R右儿子 35 int n,m,sz=0; 36 37 void build(int &rt,int l,int r)//建棵空树 38 { 39 rt=++sz;sum[rt]=0;//动态开点,初始值为0,空树 40 if(l==r){ 41 return ; 42 } 43 44 int m=(l+r)>>1; 45 build(L[rt],lson); 46 build(R[rt],rson); 47 } 48 49 void update(int pr,int &rt,int l,int r,int x) 50 { 51 rt=++sz;sum[rt]=sum[pr]+1;//插入序列,首先继承以前的线段树 然后直接单点+1就可以 52 L[rt]=L[pr];R[rt]=R[pr]; 53 if(l==r){ 54 return ; 55 } 56 57 int m=(l+r)>>1; 58 if(x<=m) update(L[pr],L[rt],lson,x);//因为右边不需要更新,所以覆盖掉左边 59 else update(R[pr],R[rt],rson,x); 60 } 61 62 int query(int pr,int rt,int l,int r,int x)//查询l到r区间就是第r次插入减去第l-1次插入后的线段树的样子 63 { 64 if(l==r){ 65 return l; 66 } 67 //因为我们建立的是权值线段树,所以直接查找就可以 68 int m=(l+r)>>1; 69 int now=sum[L[rt]]-sum[L[pr]];//now为左子树新树-旧树 70 if(now>=x) return query(L[pr],L[rt],lson,x); 71 else return query(R[pr],R[rt],rson,x-now); 72 } 73 74 int rt[maxn]; 75 76 int main() 77 { 78 int t; 79 scanf("%d",&t); 80 while(t--){ 81 scanf("%d%d",&n,&m); 82 sz=0; 83 for(int i=1;i<=n;i++){ 84 scanf("%d",&a[i]); 85 b[i]=a[i]; 86 } 87 sort(b+1,b+1+n);//首先把值全部排序去重,用于建权值线段树,权值线段树保存的内容是值的数量。 88 int d=unique(b+1,b+1+n)-(b+1); 89 build(rt[0],1,d); 90 for(int i=1;i<=n;i++){//按照序列顺序插入值 91 int x=lower_bound(b+1,b+1+d,a[i])-b; 92 update(rt[i-1],rt[i],1,d,x); 93 } 94 for(int i=1;i<=m;i++){ 95 int l,r,k; 96 scanf("%d%d%d",&l,&r,&k); 97 printf("%d\n",b[query(rt[l-1],rt[r],1,d,k)]); 98 } 99 } 100 return 0; 101 }
你来到人间,就是要看看太阳,看看外面有趣的世界。