二逼平衡樹-分塊
阿新 • • 發佈:2018-12-19
題目地址【IN-luogu】bzoj,loj也有,但是題目可能有點不同,這裡的程式碼是luogu上的AC程式碼
本來想練練樹套樹,結果一看資料範圍,就試著寫了個大力分塊,結果過了-_-||,後面再練樹套樹吧。
對於這幾個操作,用分塊的話,暴力非常好寫:
我們先將原數列分塊,對於新的分了塊的數列每個快內排個序,複雜度 。
- 查詢k在區間內的排名
邊角暴力統計,塊內二分即可。
- 查詢區間內排名為k的值
二分值,然後去統計這個值的排名即可
- 修改某一位值上的數值
在原陣列上把它改了,然後將它所在的塊還原重新排個序。
- 查詢k在區間內的前驅(前驅定義為嚴格小於x,且最大的數,若不存在輸出-2147483647)
邊角暴力,塊內二分
- 查詢k在區間內的後繼(後繼定義為嚴格大於x,且最小的數,若不存在輸出2147483647)
同理
注意:這裡邊角暴力時,必須在原陣列上查詢,不能在排好序的上面找,否則會出錯。
複雜度為 卡的過去。
醜陋程式碼新鮮出爐:
這裡我分的300大小的塊。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x7fffffff;
const int N=245,M=1e5+1,L=1e5+10;
int n,m,val[L],bef[L],up;
int stpos[N],enpos[N],bel[L],tot;
int calc_1(int l,int r,int v){
int st=l,en=r;
if(val[l]>v)return 0;
int mid,ans=r+5;
while(l<=r){
mid=l+r>>1;
if(val[mid]<v)l=mid+1,ans=mid;
else r=mid-1;
}
if(ans>en||ans<st)return 0;
return ans-st+1;
}
int find_rank(int l,int r,int k){
int ans=0;
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++){
if(bef[i]<k)++ans;
}
return ans+1;
}
for(int i=l;i<=enpos[bel[l]];i++){if(bef[i]<k)++ans;}
for(int i=stpos[bel[r]];i<=r;i++){if(bef[i]<k)++ans;}
for(int i=bel[l]+1;i<bel[r];i++){ans+=calc_1(stpos[i],enpos[i],k);}
return ans+1;
}
int solve_1(){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
return find_rank(l,r,k);
}
int solve_2(){
int L=0,R=1e8,ans=R,mid,k,l,r,now;
scanf("%d%d%d",&l,&r,&k);
while(L<=R){
mid=L+R>>1;
now=find_rank(l,r,mid);
if(now>k)R=mid-1;
else L=mid+1,ans=mid;
}
return ans;
}
void solve_3(){
int p,k;
scanf("%d%d",&p,&k);
bef[p]=k;
for(int i=stpos[bel[p]];i<=enpos[bel[p]];i++)val[i]=bef[i];
sort(val+stpos[bel[p]],val+enpos[bel[p]]+1);
}
int find_mid_4(int l,int r,int v){
int mid,ans=-inf;
while(l<=r){
mid=l+r>>1;
if(val[mid]<v){
if(val[mid]>ans)ans=val[mid];
l=mid+1;
}else r=mid-1;
}
if(val[l]<v&&val[l]>ans)ans=val[l];
return ans;
}
int find_mid_5(int l,int r,int v){
int mid,ans=inf;
while(l<=r){
mid=l+r>>1;
if(val[mid]>v){
if(val[mid]<ans)ans=val[mid];
r=mid-1;
}else l=mid+1;
}
if(val[l]>v&&val[l]<ans)ans=val[l];
return ans;
}
int solve_4(){
int l,r,k,ans=-inf;
scanf("%d%d%d",&l,&r,&k);
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++){
if(bef[i]<k){
if(bef[i]>ans)ans=bef[i];
}
}
return ans;
}
for(int i=l;i<=enpos[bel[l]];i++){
if(bef[i]<k){
if(bef[i]>ans)ans=bef[i];
}
}
for(int i=stpos[bel[r]];i<=r;i++){
if(bef[i]<k){
if(bef[i]>ans)ans=bef[i];
}
}
for(int i=bel[l]+1;i<bel[r];i++){
int now=find_mid_4(stpos[i],enpos[i],k);
if(now<k){
if(now>ans)ans=now;
}
}
return ans;
}
int solve_5(){
int l,r,k,ans=inf;
scanf("%d%d%d",&l,&r,&k);
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++){
if(bef[i]>k){
if(bef[i]<ans)ans=bef[i];
}
}
return ans;
}
for(int i=l;i<=enpos[bel[l]];i++){
if(bef[i]>k){
if(bef[i]<ans)ans=bef[i];
}
}
for(int i=stpos[bel[r]];i<=r;i++){
if(bef[i]>k){
if(bef[i]<ans)ans=bef[i];
}
}
for(int i=bel[l]+1;i<bel[r];i++){
int now=find_mid_5(stpos[i],enpos[i],k);
if(now>k){
if(now<ans)ans=now;
}
}
return ans;
}
int opt;
int main(){
scanf("%d%d",&n,&m);
int fj=0;up=n+m;
for(int i=1;i<=n;i++){
scanf("%d",&val[i]);bef[i]=val[i];
if(!fj){
fj=300;
enpos[tot]=i-1;
stpos[++tot]=i;
}
--fj;bel[i]=tot;
} enpos[tot]=n;
for(int i=1;i<=tot;i++){sort(val+stpos[i],val+enpos[i]+1);}
while(m--){
scanf("%d",&opt);
switch(opt){
case 1:{
printf("%d\n",solve_1());
break;
}
case 2:{
printf("%d\n",solve_2());
break;
}
case 3:{
solve_3();
break;
}
case 4:{
printf("%d\n",solve_4());
break;
}
case 5:{
printf("%d\n",solve_5());
break;
}
}
}
return 0;
}