傻逼怎么做:
先用一个块链维护序列,做到 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√) 个块。