zoj 2112 or bzoj1901 分析动态区间第k大的几种解法。

The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.

Your task is to write a program for this computer, which

- Reads N numbers from the input (1 <= N <= 50,000)

- Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], ..., a[j] and change some a[i] to t.

 

Input

The first line of the input is a single number X (0 < X <= 4), the number of the test cases of the input. Then X blocks each represent a single test case.

The first line of each block contains two integers N and M, representing N numbers and M instruction. It is followed by N lines. The (i+1)-th line represents the number a[i]. Then M lines that is in the following format

Q i j k or
C i t

It represents to query the k-th number of a[i], a[i+1], ..., a[j] and change some a[i] to t, respectively. It is guaranteed that at any time of the operation. Any number a[i] is a non-negative integer that is less than 1,000,000,000.

There're NO breakline between two continuous test cases.

 


<b< dd="">

Output

For each querying operation, output one integer to represent the result. (i.e. the k-th smallest number of a[i], a[i+1],..., a[j])

There're NO breakline between two continuous test cases.

 


<b< dd="">

Sample Input

2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

 


<b< dd="">

Sample Output

3
6
3
6

 

方法一:  分块方法。 分块的时间复杂度是$O(m*logn(sz+n/sz*logn))$,sz就是每块的大小。 

我们维持块内有序,然后不是完整的块就暴力查询,这里我sz用了600,好像理论上最好应该是900 。 

在zoj上跑了1200ms。  

 

 1 #include<bits/stdc++.h>
 2 using namespace std;  
 3 int const N=50000+10;  
 4 int const sz=600;  
 5 #define st(x) ((x-1)*sz+1)  
 6 #define ed(x) min(n,(x*sz))    
 7 #define bl(x) (x-1)/sz+1  
 8 int n,m,a[N],num,b[2003],ta[N];   
 9 int query(int x,int y,int z){
10     int t1=bl(x);  
11     int t2=bl(y);
12     int l=0,r=1e9;  
13     while (l<r){
14         int mid=(l+r)/2;  
15         int cnt=0;  
16         if(t1==t2){
17             for(int i=x;i<=y;i++)  
18                 if(ta[i]<=mid) cnt++;  
19         }else {
20             for(int i=x;i<=ed(t1);i++)  
21                 if(ta[i]<=mid) cnt++;  
22             for(int i=st(t2);i<=y;i++)  
23                 if(ta[i]<=mid) cnt++;  
24             for(int i=t1+1;i<=t2-1;i++){
25                 int s=st(i);  
26                 int t=ed(i);    
27                 if(a[t]<=mid) cnt+=t-s+1;  
28                 else cnt+=upper_bound(a+s,a+t+1,mid)-(a+s);   
29             }
30         }
31         if(cnt>=z) r=mid;
32         else l=mid+1; 
33     }
34     return l;  
35 }
36 void modify(int x,int y){
37     int t=bl(x); 
38     b[0]=0;  
39     int v=ta[x],cnt=1;    
40     ta[x]=y; 
41     for(int i=st(t);i<=ed(t);i++) 
42         if(a[i]!=v  || !cnt) b[++b[0]]=a[i];  
43         else cnt--;  
44     int k=ed(t)-st(t);    
45     while (k && b[k]>y){
46         b[k+1]=b[k];  
47         k--;  
48     }
49     b[k+1]=y; 
50     int tot=ed(t)-st(t)+1;   
51     for(int i=1;i<=tot;i++) 
52         a[st(t)+i-1]=b[i];  
53 }
54 int main(){
55     int cas; 
56     scanf("%d",&cas); 
57     while (cas--){
58         scanf("%d%d",&n,&m);  
59         for(int i=1;i<=n;i++)  
60             scanf("%d",&a[i]),ta[i]=a[i];  
61         num=(n-1)/sz+1;   
62         for(int i=1;i<=num;i++){
63             int x=st(i);  
64             int y=ed(i);    
65             sort(a+x,a+y+1);  
66         }
67         while (m--){
68             char s[2];   
69             int x,y,z;  
70             scanf("%s%d%d",s,&x,&y);  
71             if(s[0]=='C'){
72                 modify(x,y);  
73             }else {
74                 scanf("%d",&z);  
75                 printf("%d\n",query(x,y,z));  
76             }
77         }
78     }
79     return 0;  
80 }
View Code

 方法二:  

线段树/树状数组套平衡树 O(n*log^3n) 700+ms   sqrt(n)和logn还是差的挺远的

每个区间节点维护的是一棵平衡树,平衡树维护的是对应区间内的所有数。

查询的时候同样是二分答案mid,找到[l,r]对应所有区间节点,在每个节点的平衡树上查询<=mid的数有多少个,复杂度二分+线段树+平衡树=O(logn*logn*logn)。

修改的时候找到对应区间删除原来的数,再插入新的数即可 O(logn*logn)。

这种做法满足区间和,所以线段树可以用树状数组替换会比较快。

然后我这样写了为什么比之前的分块还要慢,虽然我自己对拍我的程序还是很快的。  

难道我平衡树写了替罪羊树的原因么。 orz。。。 

  1 #include<bits/stdc++.h>
  2 using namespace std ;  
  3 int const N=60000+10;  
  4 int const M=1000000+10;  
  5 int n,m,r[N],sz[M],lc[M],rc[M],sum,val[M],tot[M],cnt[M],top,s[N],a[N];   
  6 void update(int x){
  7     sz[x]=sz[lc[x]]+sz[rc[x]]+1;  
  8     tot[x]=cnt[x]+tot[lc[x]]+tot[rc[x]];  
  9 }  
 10 void dfs(int x){
 11     if(!x) return;  
 12     dfs(lc[x]);  
 13     if(cnt[x]) s[++top]=x;  
 14     dfs(rc[x]);  
 15 }
 16 int build(int l,int r){
 17     if(l>r) return 0;  
 18     int x=s[(l+r)/2];     
 19     if(l==r){
 20         sz[x]=1;tot[x]=cnt[x]; lc[x]=rc[x]=0;   
 21         return x;  
 22     }
 23     int mid=(l+r)/2;  
 24     lc[x]=build(l,mid-1);  
 25     rc[x]=build(mid+1,r);  
 26     update(x);  
 27     return x;    
 28 }
 29 void rebuild(int &x){
 30     top=0;  
 31     dfs(x);  
 32     x=build(1,top);    
 33 }
 34 void  ins(int &x,int v){
 35     if(!x) {
 36         x=++sum;sz[x]=1; lc[x]=rc[x]=0;val[x]=v; tot[x]=cnt[x]=1; 
 37         return;    
 38     }
 39     if(val[x]==v) {
 40         cnt[x]++;   
 41     }else {
 42         if(v<val[x]) ins(lc[x],v);  
 43         else ins(rc[x],v);  
 44     }
 45     update(x); 
 46     if(sz[x]*0.75<max(sz[lc[x]],sz[rc[x]])) rebuild(x);    
 47 }
 48 
 49 void dl(int &x,int v){
 50     if(val[x]==v) {
 51         cnt[x]--;  
 52     }else if(v<val[x]) dl(lc[x],v);  
 53     else dl(rc[x],v);  
 54     update(x);  
 55 }
 56 void  add(int x,int v){for(; x<=n; x+=x&-x)    ins(r[x],v);}
 57 void  del(int x,int v){for(; x<=n; x+=x&-x)    dl(r[x],v);}     
 58 int count(int x,int mid){
 59     if(!x) return 0;  
 60     int ret;  
 61     if(val[x]<=mid)  ret=tot[lc[x]]+cnt[x]+count(rc[x],mid);  
 62     else ret=count(lc[x],mid);  
 63     return ret;  
 64 }
 65 int calc(int x,int mid){
 66     int num=0; 
 67     for(; x; x-=x&-x) num+=count(r[x],mid);
 68     return num;  
 69 }
 70 int solve(int x,int y,int z){
 71     int l=0,r=1e9;  
 72     while (l<r){
 73         int mid=(l+r)/2;  
 74         int num=calc(y,mid)-calc(x-1,mid);      
 75         if(num>=z) r=mid; 
 76         else l=mid+1;   
 77     }
 78     return r;  
 79 }
 80 int main(){ 
 81     int cas;  
 82     scanf("%d",&cas); 
 83     while (cas--){     
 84         scanf("%d%d",&n,&m);  
 85         sum=0;   
 86         memset(r,0,sizeof(r));  
 87         for(int i=1;i<=n;i++){
 88             scanf("%d",&a[i]);  
 89             add(i,a[i]);  
 90         } 
 91         while (m--){
 92             char s[2]; 
 93             scanf("%s",s);   
 94             if(s[0]=='C'){
 95                 int x,y;  
 96                 scanf("%d%d",&x,&y);  
 97               del(x,a[x]);  
 98               add(x,a[x]=y);  
 99             } else {
100                 int x,y,z;  
101                 scanf("%d%d%d",&x,&y,&z); 
102                 printf("%d\n",solve(x,y,z));  
103             }
104         }
105     }
106     return 0;  
107 }
View Code

 方法三:  

3.按值建线段树套平衡树 O(n*log^2n) 500ms
这种做法跟第二种刚好相反,线段树是按值建的,节点[l,r]是指满足值>=l && <=r。平衡树维护的是数对应的数组下标。先将所有可能用到的值(包含初始值和修改的值)读入,然后离散化建立线段树,对于a[i](离散化后的),找到所有包含a[i]也就是l<=a[i]<=r的区间节点,将i插入到对应的平衡树中。

查询[ll,rr]的第k大的时候,对于线段树区间[l,r],先查询左孩子[l,mid]中的平衡树查找在[ll,rr]范围内的数有cnt个,假如cnt<=k,则直接递归到左孩子。cnt>k,递归到右孩子查询第k-cnt大(因为左孩子已经包含最小的cnt个了)。思想相当于整体二分吧,复杂度O(logn*logn)。

修改和2类似,复杂度O(logn*logn)

  1 #include<bits/stdc++.h>
  2 using namespace std; 
  3 int const N=20000+10;  
  4 int const M=300000+10;
  5 #define lc (x<<1) 
  6 #define rc (x<<1|1)  
  7 #define mid (l+r>>1)    
  8 struct query{
  9     int k,x,y,z;  
 10 }q[N];  
 11 int n,m,a[N],b[N],sz[M],ls[M],rs[M],cnt[M],tot[M],sum,val[M],s[N],top,rt[N<<2];  
 12 void update(int x){
 13     sz[x]=1+sz[ls[x]]+sz[rs[x]];  
 14     tot[x]=cnt[x]+tot[ls[x]]+tot[rs[x]];  
 15 }
 16 void dfs(int x){
 17     if(!x) return;  
 18     dfs(ls[x]) ;
 19     if(cnt[x]) s[++top]=x; 
 20     dfs(rs[x]); 
 21 }
 22 int build(int l,int r){
 23     if(l>r) return 0;  
 24     int x=s[mid];  
 25     if(l==r){
 26         sz[x]=1;tot[x]=cnt[x]; ls[x]=rs[x]=0;  
 27         return x;  
 28     }
 29     ls[x]=build(l,mid-1);  
 30     rs[x]=build(mid+1,r); 
 31     update(x);  
 32     return x;    
 33 }
 34 
 35 void rebuild(int &x){
 36     top=0;  
 37     dfs(x);  
 38     x=build(1,top); 
 39 }
 40 void ins(int &x,int v){
 41     if(!x) {
 42         x=++sum;sz[x]=cnt[x]=tot[x]=1; ls[x]=rs[x]=0; val[x]=v;   
 43         return;  
 44     }
 45     if(val[x]==v)  cnt[x]++;  
 46     else if(v<val[x]) ins(ls[x],v); 
 47     else ins(rs[x],v);  
 48     update(x);  
 49     if(sz[x]*0.75<max(sz[ls[x]],sz[rs[x]]))  
 50         rebuild(x);  
 51 }
 52 void insert(int x,int l,int r,int p,int v){
 53     ins(rt[x],v);  
 54     if(l==r) return;    
 55     if(p<=mid) insert(lc,l,mid,p,v);  
 56     else insert(rc,mid+1,r,p,v);  
 57 }
 58 void dl(int &x,int v){
 59     if(val[x]==v) cnt[x]--;  
 60     else  if(v<val[x])  dl(ls[x],v); 
 61     else dl(rs[x],v);  
 62     update(x);  
 63 }
 64 void del(int x,int l,int r,int p,int v){
 65     dl(rt[x],v);  
 66     if(l==r) return;  
 67     if(p<=mid) del(lc,l,mid,p,v); 
 68     else del(rc,mid+1,r,p,v);  
 69 }
 70 int count1(int x,int v){
 71     if(!x) return 0;  
 72     int ret;  
 73     if(val[x]>=v) ret=count1(ls[x],v)+tot[rs[x]]+cnt[x];  
 74     else  ret=count1(rs[x],v); 
 75     return ret; 
 76 }
 77 int count2(int x,int v){
 78     if(!x) return 0;  
 79     int ret; 
 80     if(val[x]<=v) ret=count2(rs[x],v)+tot[ls[x]]+cnt[x];  
 81     else ret=count2(ls[x],v);      
 82     return ret; 
 83 }
 84 int query(int x,int l,int r,int ll,int rr,int z){
 85     if(l>r) return 0; 
 86     if(l==r) return b[r];   
 87     int t1=count1(rt[lc],ll); 
 88     int t2=count2(rt[lc],rr);  
 89     int s=tot[rt[lc]];  
 90     int t=t2-(s-t1);                 
 91     if(t>=z) return  query(lc,l,mid,ll,rr,z);  
 92     else return query(rc,mid+1,r,ll,rr,z-t);  
 93 }
 94 int main(){
 95     int cas=1; 
 96    // scanf("%d",&cas);  
 97     while (cas--){
 98         scanf("%d%d",&n,&m);
 99         int t=n;   
100         for(int i=1;i<=n;i++)  
101             scanf("%d",&a[i]),b[i]=a[i]; 
102         for(int i=1;i<=m;i++){
103             char s[2]; 
104             scanf("%s",s);  
105             if(s[0]=='C') {
106                 q[i].k=0;  
107                 scanf("%d%d",&q[i].x,&q[i].y); 
108                 b[++t]=q[i].y;    
109             }else {
110                 q[i].k=1;  
111                 scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);  
112             }
113         } 
114         sort(b+1,b+t+1);  
115         t=unique(b+1,b+t+1)-b-1; 
116         for(int i=1;i<=n;i++){
117             int k=lower_bound(b+1,b+t+1,a[i])-b;  
118             insert(1,1,t,k,i);     
119         } 
120         for(int i=1;i<=m;i++){    
121             if(q[i].k==0) {
122                 int p=lower_bound(b+1,b+t+1,a[q[i].x])-b;  
123                 del(1,1,t,p,q[i].x);      
124                 a[q[i].x]=q[i].y; 
125                 p=lower_bound(b+1,b+t+1,a[q[i].x])-b;  
126                 insert(1,1,t,p,q[i].x);  
127             }else {
128                 printf("%d\n",query(1,1,t,q[i].x,q[i].y,q[i].z));  
129             }
130         }
131 
132     }
133     return 0; 
134 
135 }
View Code

 

posted @ 2019-07-06 21:20  zjxxcn  阅读(288)  评论(0编辑  收藏  举报