BZOJ1901 Dynamic Rankings 分块+二分法
题意:给定一个数列,维护:1、单点修改 2、查询区间第k大
题解:
找个代码debug网上全是树套树。
然而还是喜欢这么写分块,开个结构体什么的看起来很一目了然不是吗……
先把每个块排序
修改:二分找到修改的位置在块中的位置,然后移动到修改后应该在的块中的位置。
查询:确定上下界后二分答案,看每个答案有多少个数小于它代码:
#include <cmath> #include <cstdio> #include <cstring> #include <cstdlib> #include <climits> #include <iostream> #include <algorithm> using namespace std; const int MAXS=250+2; struct BLOCK{ int a[MAXS],b[MAXS]; }block[MAXS]; int N,M,S,T,U,L=INT_MAX; char s; void Build(int &S,int N){ S=ceil(sqrt(N)); for(int i=1,j=1,k=1;i<=N;i++,j++){ cin >> block[k].a[j]; block[k].b[j]=block[k].a[j]; if(j==S || i==N){ sort(block[k].b+1,block[k].b+j+1); L=min(L,block[k].b[1]-1),U=max(U,block[k].b[j]+1); k++,j=0; } } } void Update(int x,int t){ int p=(x%S?x/S+1:x/S),m=(1+S)>>1; x-=(p-1)*S; for(int l=1,r=S;l<r;m=(l+r)>>1) if(block[p].b[m]<block[p].a[x]) l=m+1; else r=m; block[p].b[m]=t; while(block[p].b[m]<block[p].b[m-1] && m!=1) swap(block[p].b[m],block[p].b[m-1]),m--; while(block[p].b[m]>block[p].b[m+1] && m!=S) swap(block[p].b[m],block[p].b[m+1]),m++; block[p].a[x]=t,U=max(U,t-1),L=min(L,t-1); } int Find(int x,int y,int k){ int ret=0,s=ceil((double)x/S),t=ceil((double)y/S); for(int i=s;i<=t;i++){ if(i==s){ int n=(s==t?y-(t-1)*S:S); for(int j=x-(s-1)*S;j<=n;j++) ret+=(block[i].a[j]<k); } else if(i==t && y%S) for(int j=1;j<=y-(t-1)*S;j++) ret+=(block[i].a[j]<k); else{ int l=1,r=S,m; while(l!=r){ m=(l+r)>>1; if(block[i].b[m]>=k) r=m; else l=m+1; } if(block[i].b[l]>=k) --l; ret+=l; } } return ret; } int Query(int x,int y,int k){ int m,l=L,r=U; while(l!=r){ m=(l+r)>>1; if(Find(x,y,m)>=k) r=m; else l=m+1; } return l-1; } int main(){ cin >> N >> M; Build(S,N); N=(N%S?N/S+1:N/S); for(int i=1,l,r,x;i<=M;i++){ cin >> s; cin >> l >> r; if(s=='C') Update(l,r); if(s=='Q'){ cin >> x; cout << Query(l,r,x) << endl; } } return 0; }