本文主要是介绍[bzoj3065]带插入区间K小值 解题报告,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
傻逼怎么做:
先用一个块链维护序列,做到 O(n√) 插入, O(1) 比较两个点在序列中位置的大小。时间复杂度是 O(nn√) 。
再用一个块链维护权值,每个块维护按位置排序的序列。查询的时候从小到大枚举每个块,先在块里二分统计块内在查询区间中的个数,然后如果发现答案在这个块里,就直接暴力找到第k小的是哪个。取块大小等于 O(nlog2n−−−−−−√) ,则时间复杂度是 O(qnlog2n−−−−−−√) 。
总的时间复杂度就是 O(nn√+qnlog2n−−−−−−√) ,然后不知道是我姿势不对还是什么情况。。写了9K。
鸟哥的做法:
用块链维护,每个块维护每种权值出现次数的前缀和、和对一块权值出现次数的前缀和。这样时间复杂度就是 O((n+q)n√) ,而且比我的做法好写很多。。
我的代码:
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cassert>
#include<cstring>
#include<cmath>
const int N=7e4+5,A=7e4+5,Q=175000+5;
int n;char * cp=(char *)malloc(30000000);
void in(int &x){bool flag=0;while(*cp<'0'||*cp>'9')flag=*cp++=='-';for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');if(flag)x=-x;
}
char * os=(char *)malloc(500000),* op=os;
void out(int x){if(x){out(x/10);*op++='0'+x%10;}
}int w[N];
int p_pos1[N],p_pos2[N];
int w_pos1[N],w_pos2[N];//只有插入
const int S=200;
//const int S=200;
//S∈[200,300]
//N/S∈[233,350]
int ptot=1;
int p_block[1000+5][1000+5];
int p_num[1000+5],p_pos0[1000+5];
void p_out(){printf("-----out p:\n");for(int i=1;i<ptot;++i){printf("%d:",p_num[i]);for(int j=1;j<=p_block[p_num[i]][0];++j)printf("%d(%d,%d) ",p_block[p_num[i]][j],p_pos1[p_block[p_num[i]][j]],p_pos2[p_block[p_num[i]][j]]);puts("");}
}
void p_build(){for(int i=1;i<=n;++i){if(p_block[ptot][0]==S)++ptot;++p_block[ptot][0];p_block[ptot][p_block[ptot][0]]=i;p_pos1[i]=ptot,p_pos2[i]=p_block[ptot][0];}for(int i=ptot++;i;--i)p_num[i]=p_pos0[i]=i;
}
void p_insert(int x){int i=1,j;for(;x-p_block[p_num[i]][0]>0&&i+1<ptot;++i)x-=p_block[p_num[i]][0];int bi=p_num[i];for(j=++p_block[bi][0];j>x;--j){p_block[bi][j]=p_block[bi][j-1];p_pos2[p_block[bi][j]]=j;}p_block[bi][x]=n;p_pos1[n]=bi,p_pos2[n]=x;if(p_block[bi][0]==S<<1){p_block[bi][0]=S;memcpy(p_block[ptot]+1,p_block[bi]+S+1,sizeof(int)*S);p_block[ptot][0]=S;for(j=S;j;--j){p_pos1[p_block[ptot][j]]=ptot;p_pos2[p_block[ptot][j]]=j;}x=i;for(i=ptot-1;i>x;--i){p_num[i+1]=p_num[i];p_pos0[p_num[i]]=i+1;}p_num[x+1]=ptot;p_pos0[ptot]=x+1;++ptot;}
}
bool p_cmp(const int &a,const int &b){return p_pos1[a]!=p_pos1[b]?p_pos0[p_pos1[a]]<p_pos0[p_pos1[b]]:p_pos2[a]<p_pos2[b];
}//需要支持插入/删除
const int B=500;
//const int B=1050;
//B∈[500,2000]
//N/B∈[35,140]
struct WS{int num,w;bool operator < (const WS & o)const{return w<o.w;}
}w_tmp[35000+5];
int w_q[2000+5],w_h,w_t;
int w_block[2000+5][2000+5],w_sorted[2000+5][2000+5];
int w_last[2000+5],w_next[2000+5],w_first;
void w_out(){printf("---out w\n");for(int i=w_first;i;i=w_next[i]){printf("w_block[%d]\n",i);printf("sorted by w:");for(int j=1;j<=w_block[i][0];++j)printf("%d ",w_block[i][j]);puts("");printf("sorted by p:");for(int j=1;j<=w_block[i][0];++j)printf("%d ",w_sorted[i][j]);puts("");}//for(int i=1;i<n;++i)printf("pos[%d]={%d,%d}\n",i,w_pos1[i],w_pos2[i]);
}
void w_build(){for(int i=1;i<=400;++i)w_q[i-1]=i;for(int i=1;i<=n;++i)w_tmp[i]=(WS){i,w[i]};sort(w_tmp+1,w_tmp+n+1);for(int i=1;i<=n;++i){if(w_block[w_q[w_h]][0]==B)++w_h;w_block[w_q[w_h]][++w_block[w_q[w_h]][0]]=w_tmp[i].num;w_pos1[w_tmp[i].num]=w_q[w_h],w_pos2[w_tmp[i].num]=w_block[w_q[w_h]][0];}w_first=1;for(int i=++w_h;i>1;--i){w_last[i]=i-1;w_next[i-1]=i;}for(int i=w_h;i;--i){memcpy(w_sorted[i]+1,w_block[i]+1,sizeof(int)*w_block[i][0]);sort(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,p_cmp);}
}
void w_split(int i){if(w_block[i][0]>=B<<1){int j;w_block[w_q[w_h]][0]=w_block[i][0]-(w_block[i][0]>>1);memcpy(w_block[w_q[w_h]]+1,w_block[i]+1+(w_block[i][0]>>1),sizeof(int)*w_block[w_q[w_h]][0]);for(j=w_block[w_q[w_h]][0];j;--j){w_pos1[w_block[w_q[w_h]][j]]=w_q[w_h];w_pos2[w_block[w_q[w_h]][j]]=j;}w_sorted[w_q[w_h]][0]=w_sorted[i][0]=0;int tmp;for(j=1;j<=w_block[i][0];++j){tmp=w_pos1[w_sorted[i][j]];w_sorted[tmp][++w_sorted[tmp][0]]=w_sorted[i][j];}w_block[i][0]>>=1;w_next[w_q[w_h]]=w_next[i];w_last[w_q[w_h]]=i;w_last[w_next[i]]=w_q[w_h];w_next[i]=w_q[w_h];w_h=(w_h+1)%400;}
}
int tmp[2000+5];
void w_delete(int x){int i=w_pos1[x],j;for(j=w_pos2[x];j<w_block[i][0];++j){w_block[i][j]=w_block[i][j+1];w_pos2[w_block[i][j]]=j;}j=w_block[i][0];while(w_sorted[i][j]!=x)--j;for(;j<w_block[i][0];++j)w_sorted[i][j]=w_sorted[i][j+1];--w_block[i][0];if(w_block[i][0]){if(w_block[i][0]<B&&w_next[i]){memcpy(w_block[i]+w_block[i][0]+1,w_block[w_next[i]]+1,sizeof(int)*w_block[w_next[i]][0]);memcpy(w_block[w_next[i]]+1,w_block[i]+1,sizeof(int)*(w_block[i][0]+w_block[w_next[i]][0]));for(j=w_block[i][0];j;--j)w_pos1[w_block[i][j]]=w_next[i];for(j=w_block[w_next[i]][0]+w_block[i][0];j>w_block[i][0];--j)w_pos2[w_block[w_next[i]][j]]=j;int k=1,o=1;for(j=1;j<=w_block[i][0]&&k<=w_block[w_next[i]][0];)if(p_cmp(w_sorted[i][j],w_sorted[w_next[i]][k])){tmp[o++]=w_sorted[i][j++];}else{tmp[o++]=w_sorted[w_next[i]][k++];}memcpy(tmp+o,w_sorted[i]+j,sizeof(int)*(w_block[i][0]-j+1));memcpy(tmp+o,w_sorted[w_next[i]]+k,sizeof(int)*(w_block[w_next[i]][0]-k+1));w_block[w_next[i]][0]+=w_block[i][0];memcpy(w_sorted[w_next[i]]+1,tmp+1,sizeof(int)*w_block[w_next[i]][0]);w_next[w_last[i]]=w_next[i];w_last[w_next[i]]=w_last[i];if(i==w_first)w_first=w_next[i];w_q[w_t]=i;w_t=(w_t+1)%400;w_split(w_next[i]);}}else{w_next[w_last[i]]=w_next[i];w_last[w_next[i]]=w_last[i];if(i==w_first)w_first=w_next[i];w_q[w_t]=i;w_t=(w_t+1)%400;}
}
void w_insert(int x){int i=w_first,j;if(i==0){i=w_first=w_q[w_h];w_h=(w_h+1)%400;w_last[i]=w_next[i]=0;w_block[i][0]=1,w_block[i][1]=x;w_sorted[i][1]=x;w_pos1[x]=i,w_pos2[x]=1;}else{while(w_next[i]&&w[w_block[w_next[i]][1]]<w[x])i=w_next[i];for(j=++w_block[i][0];j>1&&w[w_block[i][j-1]]>=w[x];--j){w_block[i][j]=w_block[i][j-1];w_pos2[w_block[i][j]]=j;}w_block[i][j]=x;w_pos1[x]=i,w_pos2[x]=j;for(j=w_block[i][0];j>1&&p_cmp(x,w_sorted[i][j-1]);--j)w_sorted[i][j]=w_sorted[i][j-1];w_sorted[i][j]=x;w_split(i);}
}
int main(){freopen("bzoj_3065.in","r",stdin);freopen("bzoj_3065.out","w",stdout);fread(cp,1,30000000,stdin);int m;in(n);for(int i=1;i<=n;++i)in(w[i]);p_build();w_build();++n;//p_out(),w_out();int q;in(q);int x,y,k,val;int i,j;int lastans=0;int cnt;while(q--){while(*cp<'A'||*cp>'Z')++cp;//printf("-------------\n");switch(*cp++){case 'Q':in(x),in(y),in(k);//x^=lastans,y^=lastans,k^=lastans;for(i=1;x>p_block[p_num[i]][0];++i){x-=p_block[p_num[i]][0];y-=p_block[p_num[i]][0];}x=p_block[p_num[i]][x];for(;y>p_block[p_num[i]][0];++i)y-=p_block[p_num[i]][0];y=p_block[p_num[i]][y];//printf("Get range:[%d,%d]\n",x,y);for(i=w_first;;i=w_next[i]){cnt=upper_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,y,p_cmp)-lower_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,x,p_cmp);//printf("cnt(%d)=%d-%d\n",i,upper_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,y,p_cmp)-w_sorted[i],lower_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,x,p_cmp)-w_sorted[i]);if(k>cnt)k-=cnt;else{for(j=1;j<=w_block[i][0];++j)if(!p_cmp(w_block[i][j],x)&&!p_cmp(y,w_block[i][j])&&--k==0){lastans=w[w_block[i][j]];break;}break;}}if(lastans)out(lastans);else *op++='0';*op++='\n';//printf("%d\n",lastans);break;case 'M':in(x),in(val);//x^=lastans,val^=lastans;for(i=1;x>p_block[p_num[i]][0];++i)x-=p_block[p_num[i]][0];x=p_block[p_num[i]][x];w_delete(x);w[x]=val;w_insert(x);break;case 'I':in(x),in(val);//x^=lastans,val^=lastans;w[n]=val;p_insert(x);w_insert(n);++n;break;}//p_out(),w_out();}fwrite(os,1,op-os,stdout);
}
总结:
①一定要生成极限数据测试!
②注意发掘题目的性质——既然权值是静态的,而位置是动态的,那么我们为什么要用块链维护权值呢?
③块链其实可以在每个块维护 O(n) 的信息,因为它最多只会产生 O(n√) 个块。如果要删除的话也没关系,我们在删除的时候其实并不需要把比较小的块合并,因为即使不那样也只会有 O(n√) 个块。
这篇关于[bzoj3065]带插入区间K小值 解题报告的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!