带插入区间K小值 - 解题报告
这题真毒,BZOJ过了,Luogu没过(请求增长时限QwQ)
题意(三种操作):
- 插入一个数到数列中
- 修改数列中一个数的值
- 询问区间第\(k\)小
强制在线。
然后可怜的我只会 \(O(n \log^3 n)\) 的做法QAQ……
这道题可以树套树,由于需要动态插点,所以外层树是平衡树;由于需要询问区间\(k\)小值,所以内层树可以是权值线段树(不嫌烦可以写平衡树),维护外层树所在子树的权值信息,显然如果外层树需要旋转操作,维护内层树将相当麻烦,所以我建议外层树写替罪羊树。
对于插入和修改操作,可以根据二叉搜索树的做法进行,同时子树不平衡时暴力重构。
对于查询操作,用线段树上查询区间的方法用vector存储需要访问的区间,显然最多有\(\log n\)个区间,再二分答案计算(类似主席树的查询操作)。
目前时间的瓶颈在于重构部分暴力点权插入线段树需要\(O(n \log^3 n)\),使得Luogu上\(TLE\),BZOJ上\(AC\)。
然后是代码(\(TLE\)):
#include<bits/stdc++.h>
#define ratio 4
using namespace std;
inline int read() {
register int tmp=0;register char c=getchar(); while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') tmp=(tmp<<1)+(tmp<<3)+(c^48),c=getchar(); return tmp;
}
inline void print(int x) { if(x>9) print(x/10); putchar(x%10+'0'); }
const int N=70010;
int n,m,a[N],ans;
vector <int> q,ttt;
int root;
int Tot=0;
struct ST {
int l,r,s;
ST(int L,int R,int S):l(L),r(R),s(S) {}
ST() {}
};ST tr[20000010]; int Sta[20000010],Top=0;
int tot=0;
struct P {
int l,r,val,rt,sz;
P(int L,int R,int V,int Rt,int S):l(L),r(R),val(V),rt(Rt),sz(S) {}
P() {}
};P t[N];
int sta[N],top=0;
inline int Min(const int x,const int y) { return x<y? x:y; }
inline int Max(const int x,const int y) { return x>y? x:y; }
inline int st_node() {
if(!Top) return ++Tot; --Top; return Sta[Top+1];
}
void clean(int &u) {
if(tr[u].l) clean(tr[u].l); if(tr[u].r) clean(tr[u].r);
Sta[++Top]=u,tr[u]=ST(0,0,0),u=0;
}
void st_insert(int &u,int l,int r,int x,int w) {
if(!u) u=st_node(); tr[u].s+=w;
if(l>=r) { if(!tr[u].s) clean(u); return ; }
int mid=(l+r)>>1;
x<=mid? st_insert(tr[u].l,l,mid,x,w):
st_insert(tr[u].r,mid+1,r,x,w);
if(!tr[u].s) clean(u);
}
void debug(int u,int Tab) {
if(t[u].l) debug(t[u].l,Tab+1);
for(int i=1;i<Tab;i++) printf(" |");
printf(" %d\n",t[u].val);
if(t[u].r) debug(t[u].r,Tab+1);
}
inline bool Bad(int u) {
return (double)t[t[u].l].sz>ratio*(double)t[t[u].r].sz||
(double)t[t[u].r].sz>ratio*(double)t[t[u].l].sz;
}
inline int sgt_node(int l,int r,int v,int sz) {
t[sta[top]]=P(l,r,v,0,sz),--top; return sta[top+1];
}
void sgt_init(int &u,int l,int r) {
int mid=(l+r)>>1; u=sgt_node(0,0,a[mid],r-l+1);
for(int i=l;i<=r;i++) st_insert(t[u].rt,0,70000,a[i],1);
if(l<mid) sgt_init(t[u].l,l,mid-1);
if(mid<r) sgt_init(t[u].r,mid+1,r);
}
void ask(int u,int l,int r,int L,int R) {
if(L>R||l>r) return ;
if(l==L&&r==R) { q.push_back(u);return ; }
int mid=t[t[u].l].sz+l;
if(L<=mid&&mid<=R) ttt.push_back(t[u].val);
if(R<mid) ask(t[u].l,l,mid-1,L,R);
else if(L>mid) ask(t[u].r,mid+1,r,L,R);
else ask(t[u].l,l,mid-1,L,mid-1),ask(t[u].r,mid+1,r,mid+1,R);
}
int solve(int l,int r,int k) {
ask(root,1,t[root].sz,l,r); int L=0,R=70000,mid,sum;
for(int i=0;i<q.size();i++) q[i]=t[q[i]].rt;
// printf("ASK : ");
// for(int i=0;i<q.size();i++) printf(" %d",tr[q[i]].s);printf("\n");
// for(int i=0;i<ttt.size();i++) printf(" 1",ttt[i]);printf("\n");
while(L<R) {
mid=(L+R)>>1,sum=0;
for(int i=0;i<q.size();i++) sum+=tr[tr[q[i]].l].s;
for(int i=0;i<ttt.size();i++) if(ttt[i]<=mid) ++sum;
// printf("MWHAKIOI : %d %d\n",mid,sum);
if(sum>=k) {
R=mid; for(int i=0;i<q.size();i++) q[i]=tr[q[i]].l;
}
else {
k-=sum,L=mid+1;
for(int i=0;i<q.size();i++) q[i]=tr[q[i]].r;
for(int i=0;i<ttt.size();i++)
if(ttt[i]<=mid) ttt[i]=70010;
}
}
q.clear(),ttt.clear(); return R;
}
int sgt_find(int u,int k) {
if(t[t[u].l].sz+1==k) return t[u].val;
if(k<=t[t[u].l].sz) return sgt_find(t[u].l,k);
return sgt_find(t[u].r,k-t[t[u].l].sz-1);
}
void sgt_mdy(int u,int k,int Old,int New) {
st_insert(t[u].rt,0,70000,Old,-1),
st_insert(t[u].rt,0,70000,New,1);
if(t[t[u].l].sz+1==k) { t[u].val=New;return ; }
k<=t[t[u].l].sz? sgt_mdy(t[u].l,k,Old,New):
sgt_mdy(t[u].r,k-t[t[u].l].sz-1,Old,New);
}
void dfs(int &u) {
if(t[u].l) dfs(t[u].l);
a[++n]=t[u].val,clean(t[u].rt),sta[++top]=u;
if(t[u].r) dfs(t[u].r);
u=0;
}
void rebuild(int &u) { n=0,dfs(u),sgt_init(u,1,n); }
void sgt_insert(int &u,int x,int w) {
if(!u) {
u=sgt_node(0,0,w,1),st_insert(t[u].rt,0,70000,w,1);
return ;
}
st_insert(t[u].rt,0,70000,w,1),++t[u].sz;
if(x<=t[t[u].l].sz) sgt_insert(t[u].l,x,w);
else sgt_insert(t[u].r,x-t[t[u].l].sz-1,w);
if(!Bad(u)) {
if(Bad(t[u].l)) rebuild(t[u].l);
if(Bad(t[u].r)) rebuild(t[u].r);
}
}
int main() {
n=read();
for(register int i=1;i<=n;++i) a[i]=read(); m=read();
for(register int i=1;i<N;++i) sta[++top]=i;
sgt_init(root,1,n);
// printf("Array : \n"),debug(root,1),printf("\n");
for(;m;--m) {
register char opt=getchar();
while(opt<'A'||opt>'Z') opt=getchar();
if(opt=='Q') {
register int l=read()^ans,r=read()^ans,k=read()^ans;
ans=solve(l,r,k),print(ans),putchar(10);
}
else if(opt=='M') {
register int x=read()^ans,y=read()^ans,z;
z=sgt_find(root,x),sgt_mdy(root,x,z,y);
}
else {
register int x=read()^ans,y=read()^ans;
sgt_insert(root,x-1,y); if(Bad(root)) rebuild(root);
}
// printf("Array : \n"),debug(root,1),printf("\n");
}
return 0;
}
目前想到的解决方式:
- 线段树合并
- leafy tree 实现重量平衡树(线段树合并)
(然而这么做好像有问题)
被卡的代码:
#include<bits/stdc++.h>
#define ratio 3
using namespace std;
inline int read() {
register int tmp=0;register char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') tmp=(tmp<<1)+(tmp<<3)+(c^48),c=getchar();
return tmp;
}
inline void print(int x) { if(x>9) print(x/10); putchar(x%10+'0'); }
const int N=70010;
int n,m,a[N],ans;
vector <int> q,ttt;
int root;
int Tot=0;
struct ST {
int l,r,s;
ST(int L,int R,int S):l(L),r(R),s(S) {}
ST() {}
};ST tr[20000010]; int Sta[20000010],Top=0;
int tot=0;
struct P {
int l,r,val,rt,sz;
P(int L,int R,int V,int Rt,int S):l(L),r(R),val(V),rt(Rt),sz(S) {}
P() {}
};P t[N];
int sta[N],top=0;
inline int Min(const int &x,const int &y) { return x<y? x:y; }
inline int Max(const int &x,const int &y) { return x>y? x:y; }
inline int st_node() {
if(!Top) return ++Tot; --Top; return Sta[Top+1];
}
void clean(int &u) {
if(tr[u].l) clean(tr[u].l); if(tr[u].r) clean(tr[u].r);
Sta[++Top]=u,tr[u]=ST(0,0,0),u=0;
}
void st_insert(int &u,int l,int r,int x,int w) {
if(!u) u=st_node(); tr[u].s+=w;
if(l>=r) { if(!tr[u].s) clean(u); return ; }
int mid=(l+r)>>1;
x<=mid? st_insert(tr[u].l,l,mid,x,w):
st_insert(tr[u].r,mid+1,r,x,w);
if(!tr[u].s) clean(u);
}
void debug(int u,int Tab) {
if(t[u].l) debug(t[u].l,Tab+1);
for(int i=1;i<Tab;i++) printf(" |");
printf(" %d\n",t[u].val);
if(t[u].r) debug(t[u].r,Tab+1);
}
inline bool Bad(int u) {
return (double)t[t[u].l].sz>ratio*(double)t[t[u].r].sz||
(double)t[t[u].r].sz>ratio*(double)t[t[u].l].sz;
}
inline int sgt_node(int l,int r,int v,int sz) {
t[sta[top]]=P(l,r,v,0,sz),--top; return sta[top+1];
}
void Merge(int &u,int l,int r,int L,int R) {
if(!l&&!r) return ;
u=st_node(),tr[u].s=tr[l].s+tr[r].s;
if(l==r) return ;
int mid=(L+R)>>1;
Merge(tr[u].l,tr[l].l,tr[r].l,L,mid),
Merge(tr[u].r,tr[l].r,tr[r].r,mid+1,r);
}
void sgt_init(int &u,int l,int r) {
int mid=(l+r)>>1; u=sgt_node(0,0,a[mid],r-l+1);
if(l<mid) sgt_init(t[u].l,l,mid-1);
if(mid<r) sgt_init(t[u].r,mid+1,r);
Merge(t[u].rt,t[t[u].l].rt,t[t[u].r].rt,0,70000);
st_insert(t[u].rt,0,70000,a[mid],1);
}
void ask(int u,int l,int r,int L,int R) {
if(L>R||l>r) return ;
if(l==L&&r==R) { q.push_back(u);return ; }
int mid=t[t[u].l].sz+l;
if(L<=mid&&mid<=R) ttt.push_back(t[u].val);
if(R<mid) ask(t[u].l,l,mid-1,L,R);
else if(L>mid) ask(t[u].r,mid+1,r,L,R);
else ask(t[u].l,l,mid-1,L,mid-1),ask(t[u].r,mid+1,r,mid+1,R);
}
int solve(int l,int r,int k) {
ask(root,1,t[root].sz,l,r); int L=0,R=70000,mid,sum;
for(int i=0;i<q.size();i++) q[i]=t[q[i]].rt;
// printf("ASK : ");
// for(int i=0;i<q.size();i++) printf(" %d",tr[q[i]].s);printf("\n");
// for(int i=0;i<ttt.size();i++) printf(" 1",ttt[i]);printf("\n");
while(L<R) {
mid=(L+R)>>1,sum=0;
for(int i=0;i<q.size();i++) sum+=tr[tr[q[i]].l].s;
for(int i=0;i<ttt.size();i++) if(ttt[i]<=mid) ++sum;
// printf("MWHAKIOI : %d %d\n",mid,sum);
if(sum>=k) {
R=mid; for(int i=0;i<q.size();i++) q[i]=tr[q[i]].l;
}
else {
k-=sum,L=mid+1;
for(int i=0;i<q.size();i++) q[i]=tr[q[i]].r;
for(int i=0;i<ttt.size();i++)
if(ttt[i]<=mid) ttt[i]=70010;
}
}
q.clear(),ttt.clear(); return R;
}
int sgt_find(int u,int k) {
if(t[t[u].l].sz+1==k) return t[u].val;
if(k<=t[t[u].l].sz) return sgt_find(t[u].l,k);
return sgt_find(t[u].r,k-t[t[u].l].sz-1);
}
void sgt_mdy(int u,int k,int Old,int New) {
st_insert(t[u].rt,0,70000,Old,-1),
st_insert(t[u].rt,0,70000,New,1);
if(t[t[u].l].sz+1==k) { t[u].val=New;return ; }
k<=t[t[u].l].sz? sgt_mdy(t[u].l,k,Old,New):
sgt_mdy(t[u].r,k-t[t[u].l].sz-1,Old,New);
}
void dfs(int &u) {
if(t[u].l) dfs(t[u].l);
a[++n]=t[u].val,clean(t[u].rt),sta[++top]=u;
if(t[u].r) dfs(t[u].r);
u=0;
}
void rebuild(int &u) { n=0,dfs(u),sgt_init(u,1,n); }
void sgt_insert(int &u,int x,int w) {
if(!u) {
u=sgt_node(0,0,w,1),st_insert(t[u].rt,0,70000,w,1);
return ;
}
st_insert(t[u].rt,0,70000,w,1),++t[u].sz;
if(x<=t[t[u].l].sz) sgt_insert(t[u].l,x,w);
else sgt_insert(t[u].r,x-t[t[u].l].sz-1,w);
if(!Bad(u)) {
if(Bad(t[u].l)) rebuild(t[u].l);
if(Bad(t[u].r)) rebuild(t[u].r);
}
}
int main() {
n=read();
for(register int i=1;i<=n;++i) a[i]=read(); m=read();
for(register int i=1;i<N;++i) sta[++top]=i;
sgt_init(root,1,n);
// printf("Array : \n"),debug(root,1),printf("\n");
for(;m;--m) {
register char opt=getchar();
while(opt<'A'||opt>'Z') opt=getchar();
if(opt=='Q') {
register int l=read()^ans,r=read()^ans,k=read()^ans;
ans=solve(l,r,k),print(ans),putchar(10);
}
else if(opt=='M') {
register int x=read()^ans,y=read()^ans,z;
z=sgt_find(root,x),sgt_mdy(root,x,z,y);
}
else {
register int x=read()^ans,y=read()^ans;
sgt_insert(root,x-1,y); if(Bad(root)) rebuild(root);
}
// printf("Array : \n"),debug(root,1),printf("\n");
}
return 0;
}
然后仅剩下的思路是块状链表,若能做到\(O(n \sqrt {n})\) 同样理论可过。
我尝试去敲块状链表,由于常数优秀,跑得比\(O(n \log^2 n)\)还快,Luogu上过了60%的数据。听说某位大神用块状链表卡进去了,吾辈表示无能为力。
写题没有必要做对,写题的目的是锻炼思维和解题方式,留下思考的痕迹,这样写题才有意义。
最后我在这里留下块状链表写法的代码。
#include<bits/stdc++.h>
#define ONLINE_JUDGE
#define re register
using namespace std;
const int N=70000,BLOSIZE=1100,MXBLO=2*N/BLOSIZE;
int n,m;
struct P {
int a[3*BLOSIZE+100],b[3*BLOSIZE+100],sz,to;
void reset() {
for(re int i=1;i<=sz;++i) b[i]=a[i];
sort(b+1,b+sz+1);
}
};P t[MXBLO+100];
int tot=0;
inline int read() {
re int x=0;re char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void print(int x) { if(x>9) print(x/10); putchar(x%10+'0'); }
inline void insert(int x,int w) {
re int u,tt;
for(u=0;u!=-1&&x>t[u].sz;u=t[u].to) x-=t[u].sz;
for(re int i=t[u].sz;i>=x;--i) t[u].a[i+1]=t[u].a[i];
t[u].a[x]=w,++t[u].sz;
if(t[u].sz>=2*BLOSIZE) {
int nu=++tot; t[nu].to=t[u].to,t[u].to=nu;
t[nu].sz=t[u].sz-BLOSIZE;
for(re int i=BLOSIZE+1;i<=t[u].sz;++i)
t[nu].a[i-BLOSIZE]=t[nu].b[i-BLOSIZE]=t[u].a[i];
for(re int i=1;i<=BLOSIZE;++i) t[u].b[i]=t[u].a[i];
t[u].sz=BLOSIZE;
sort(t[nu].b+1,t[nu].b+t[nu].sz+1),
sort(t[u].b+1,t[u].b+t[u].sz+1); return ;
}
tt=t[u].sz-1;
for(;tt;--tt) {
if(t[u].b[tt]>w) t[u].b[tt+1]=t[u].b[tt];
else break;
}
t[u].b[tt+1]=w;
}
void modify(int x,int w) {
re int u,tt;
for(u=0;u!=-1&&x>t[u].sz;u=t[u].to) x-=t[u].sz;
for(re int i=1;i<=t[u].sz;++i)
if(t[u].b[i]==t[u].a[x]) {
for(re int j=i;j<t[u].sz;++j)
t[u].b[j]=t[u].b[j+1];
t[u].b[t[u].sz--]=0;break;
}
t[u].a[x]=w,tt=t[u].sz;
for(;tt;--tt) {
if(t[u].b[tt]>w) t[u].b[tt+1]=t[u].b[tt];
else break;
}
t[u].b[tt+1]=w,++t[u].sz;
}
int query(int x,int y,int w) {
re int u,v,sum=0,l,r,mid,tt;
for(u=0;u!=-1&&x>t[u].sz;u=t[u].to) x-=t[u].sz;
for(v=0;v!=-1&&y>t[v].sz;v=t[v].to) y-=t[v].sz;
if(u==v) {
for(int i=x;i<=y;i++) if(t[u].a[i]<w) ++sum;
return sum;
}
for(re int i=x;i<=t[u].sz;++i) if(t[u].a[i]<w) ++sum;
for(re int i=1;i<=y;++i) if(t[v].a[i]<w) ++sum;
for(re int i=t[u].to;i!=v;i=t[i].to) {
l=1,r=t[i].sz,tt=0;
while(l<=r) {
mid=(l+r)>>1;
t[i].b[mid]<w? (tt=mid,l=mid+1):r=mid-1;
}
sum+=tt;
}
return sum;
}
void init() {
t[0].to=-1,t[0].sz=0,n=read(); int cnt=BLOSIZE,Old=0;
for(re int i=1;i<=n;++i) {
if(cnt==BLOSIZE) {
int u=++tot;
t[u].to=t[Old].to,t[Old].to=u,
sort(t[Old].b+1,t[Old].b+t[Old].sz+1);
Old=u,cnt=0;
}
t[Old].a[++t[Old].sz]=read(),
t[Old].b[t[Old].sz]=t[Old].a[t[Old].sz],
++cnt;
}
if(cnt==BLOSIZE) {
int u=++tot;
t[u].to=t[Old].to,t[Old].to=u,
sort(t[Old].b+1,t[Old].b+t[Old].sz+1);
Old=u,cnt=0;
}
t[Old].a[++t[Old].sz]=70000,
t[Old].b[t[Old].sz]=t[Old].a[t[Old].sz],
sort(t[Old].b+1,t[Old].b+t[Old].sz+1);
m=read();
}
void debug() {
for(int i=0;i!=-1;i=t[i].to) {
printf(" ( [%d]",i);
for(int j=1;j<=t[i].sz;++j)
printf(" %d",t[i].b[j]);
printf(" )");
}
printf("\n");
}
int main() {
#ifndef ONLINE_JUDGE
freopen("Dynamic Kth in range.in","r",stdin);
freopen("Dynamic Kth in range.out","w",stdout);
int cur=clock();
#endif
char c; int x,y,k,ans=0,l,r,mid; init();
for(;m;--m) {
c=getchar(); while(c<'A'||c>'Z') c=getchar();
switch(c) {
case 'Q':
x=read()^ans,y=read()^ans,k=read()^ans;
l=0,r=70000;
while(l<=r) {
mid=(l+r)>>1;
query(x,y,mid)<k?
(l=mid+1,ans=mid):r=mid-1;
}
print(ans),putchar(10);break;
case 'I':
x=read()^ans,y=read()^ans,insert(x,y);break;
default: x=read()^ans,y=read()^ans,modify(x,y);
}
#ifndef ONLINE_JUDGE
ans=0;
#endif
}
#ifndef ONLINE_JUDGE
printf(">>> %d ms.",clock()-cur);
fclose(stdin),fclose(stdout);
#endif
return 0;
}