1 条题解
-
0
自动搬运
来自洛谷,原作者为

daniel14311531
文化课菜逼搬运于
2025-08-24 21:58:27,当前版本为作者最后更新于2018-12-24 20:17:47,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这题真毒,BZOJ过了,Luogu没过(请求增长时限QwQ)
题意(三种操作):
- 插入一个数到数列中
- 修改数列中一个数的值
- 询问区间第小
强制在线。
然后可怜的我只会 的做法QAQ……
这道题可以树套树,由于需要动态插点,所以外层树是平衡树;由于需要询问区间小值,所以内层树可以是权值线段树(不嫌烦可以写平衡树),维护外层树所在子树的权值信息,显然如果外层树需要旋转操作,维护内层树将相当麻烦,所以我建议外层树写替罪羊树。
对于插入和修改操作,可以根据二叉搜索树的做法进行,同时子树不平衡时暴力重构。
对于查询操作,用线段树上查询区间的方法用vector存储需要访问的区间,显然最多有个区间,再二分答案计算(类似主席树的查询操作)。
目前时间的瓶颈在于重构部分暴力点权插入线段树需要,使得Luogu上,BZOJ上。
然后是代码():#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; //sgt_...:替罪羊树所属函数 //st_...:权值线段树所属函数 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 实现重量平衡树(线段树合并)
我们知道线段树合并的时间复杂度是 加上重构的时间后为 ,理论可过。但实际上在Luogu依旧被卡(1.2 s)。
被卡的代码:
#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; //sgt_...:替罪羊树所属函数 //st_...:权值线段树所属函数 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; }然后仅剩下的思路是块状链表,若能做到 同样理论可过。
我尝试去敲块状链表,由于常数优秀,跑得比还快,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; }再次请求管理员增长时限!
- 1
信息
- ID
- 3285
- 时间
- 2000ms
- 内存
- 500MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者