HDU 2665.Kth number-无修改区间第K小-可持久化线段树(主席树)模板

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]
 
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 }

你来到人间,就是要看看太阳,看看外面有趣的世界。

 

猜你喜欢

转载自www.cnblogs.com/ZERO-/p/9806378.html