整体二分学习笔记
整体二分
就是一种把答案二分,计算对下标的贡献的离线做法
核心函数solve
inline void solve(int ql,int qr,int l,int r) //ql与qr为下标 { if(qr<ql||r<l) return; //防止死循环 if(l==r) //l=r时候说明从ql到qr下标的都是这个排名 { for(int i=ql;i<=qr;++i) { if(q[i].tp) ans[q[i].id]=l; } return; } int mid=(l+r)>>1; int p1=0,p2=0; for(int i=ql;i<=qr;++i) { if(!q[i].tp) //插入操作 { if(mid>=q[i].x) //插入的值小于二分的值,就递归到左边,然后别忘了左边的数会对右边产生排名的影响,所以用树状数组记录 { add(q[i].id,1); q1[++p1]=q[i]; } else q2[++p2]=q[i]; } else //查询操作 { int tmp=query(q[i].y)-query(q[i].x-1); //l,r内有多少数在左区间 if(tmp>=q[i].k) //数量大于当前想查的排名,那就放到左边 { q1[++p1]=q[i]; } else //放到右边,同时在右边查询的排名应该减去左边比他小的数 { q[i].k-=tmp; q2[++p2]=q[i]; } } } for(int i=1;i<=p1;++i) if(!q1[i].tp) add(q1[i].id,-1); //把树状数组清空 for(int i=1;i<=p1;++i) q[i+ql-1]=q1[i]; //把临时处理结果保存回原本序列更新答案 for(int i=1;i<=p2;++i) q[i+ql-1+p1]=q2[i]; solve(ql,ql+p1-1,l,mid); solve(ql+p1,qr,mid+1,r);// p1大小即为左边边需要递归处理的下标区间长度 }
emm我觉得挺显然的了
以上代码来自
#include<bits/stdc++.h> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } const int inf=1e9+7; const int maxn=2e5+10; int ans[maxn],cnt=0; struct node { int x,y,k,id,tp; }q[maxn<<1],q1[maxn<<1],q2[maxn<<1]; int tree[maxn<<1]; int n,m; inline int lowbit(int x) { return (x&(-x)); } inline void add(int x,int k) { for(;x<=n;x+=lowbit(x)) tree[x]+=k; } inline int query(int x) { int ans=0; for(;x;x-=lowbit(x)) ans+=tree[x]; return ans; } inline void solve(int ql,int qr,int l,int r) { if(qr<ql||r<l) return; if(l==r) { for(int i=ql;i<=qr;++i) { if(q[i].tp) ans[q[i].id]=l; } return; } int mid=(l+r)>>1; int p1=0,p2=0; for(int i=ql;i<=qr;++i) { if(!q[i].tp) { if(mid>=q[i].x) { add(q[i].id,1); q1[++p1]=q[i]; } else q2[++p2]=q[i]; } else { int tmp=query(q[i].y)-query(q[i].x-1); if(tmp>=q[i].k) { q1[++p1]=q[i]; } else { q[i].k-=tmp; q2[++p2]=q[i]; } } } for(int i=1;i<=p1;++i) if(!q1[i].tp) add(q1[i].id,-1); for(int i=1;i<=p1;++i) q[i+ql-1]=q1[i]; for(int i=1;i<=p2;++i) q[i+ql-1+p1]=q2[i]; solve(ql,ql+p1-1,l,mid); solve(ql+p1,qr,mid+1,r); } int main() { n=read(); m=read(); for(register int i=1;i<=n;++i) { int x; scanf("%d",&x); q[++cnt]=(node){x,1,inf,i,0}; } for(register int i=1;i<=m;++i) { int x,y,z; scanf("%d%d%d",&x,&y,&z); q[++cnt]=(node){x,y,z,i,1}; } solve(1,cnt,-inf,inf); for(register int i=1;i<=m;++i) { printf("%d\n",ans[i]); } } /* 5 5 25957 6405 15770 26287 26465 2 2 1 3 4 1 4 5 1 1 2 2 4 4 1 */
然后就是修改,
修改操作其实就是把一个数先删除,再插入另一个数
接下来由于时间单调(不会对之前的产生影响
所以可以乱搞了