题解 P3380 【【模板】二逼平衡树(树套树)】
说说此题我知道的几种方法
可修改区间k大的骗分做法:分块。
分成根号n个块,块内排序。
每个数在块内便可二分查找前驱,从而得到多少个数比他小。
块外部分暴力检验。
每次二分至一个值i,使得<i-1的数<k,<i的数<=k。
一次时间O(log范围*(根号n*log(根号n)))
暴力做法:跟上一种类似,线段树套平衡树
正解:
1 整体二分
就是二分答案跑cdq,
对于查询k大,我们得到左边的个数num,如果k<=num,去左边,否则k-=num,去右边
否则k<=mid分左,否则分右边。
对于查询rank,我们只在k>mid的时候,用num更新答案。
对于查询前驱,我们只在k>mid的时候,用左边的对应区间max更新答案(线段树维护即可)。
(我通过将所有数取反来将后继转化为前驱处理。)
此法较好实现。
设U为数的范围,时间(n+m)*logUlogn。离散后应该是(n+m)*lognlogn,但我懒得离散。。
目前拿了这题洛谷的rank2。
核心部分:
void solve(int L,int R,int l,int r)
{
if (l>r) return;
if (L==R)
{
for (i=l;i<=r;++i)
{
x=q[i];
if (a[x].type==2) ans[a[x].id]=L;
}
return;
}
int mid=(L+R)>>1;
t1=t2=t3=0;
for (i=l;i<=r;++i)
{
now=q[i];
if (a[now].type==2)
{
num=T.qiu_s(a[now].l,a[now].r);
if (num<a[now].x) { a[now].x-=num;q2[++t2]=now; }
else q1[++t1]=now;
} else
{
if (a[now].x>mid) q2[++t2]=now;
else q1[++t1]=now;
if (a[now].type==3)
{
if (a[now].x<=mid)
{
q3[++t3]=now;
T.add(a[now].l,a[now].id,a[now].x);
}
} else
if (a[now].x>mid)
if (a[now].type==4)
{
chmax(ans[a[now].id],T.qiu_m(a[now].l,a[now].r));
} else
ans[a[now].id]+=T.qiu_s(a[now].l,a[now].r);
}
}
while (t3) { now=q3[t3--];T.clear(a[now].l); }
int k=l-1;
for (i=1;i<=t1;++i) q[++k]=q1[i];
for (i=1;i<=t2;++i) q[k+i]=q2[i];
solve(L,mid,l,k);solve(mid+1,R,k+1,r);
}
2树状数组套动态加点线段树
不知道为什么很多人说是可持久化线段树。其实我们是对树状数组每个节点套了个线段树,彼此之间并没有关联的。
想法就是,假如我们得到了[l,r]的权值线段树,那么显然一切操作都能logn做了。
由于我们线段树维护的是权值区间内的点的个数,这是可以加和的。
所以如果我们给每个点建个线段树,那么就是询问[l,r]的线段树的和。
那么实际上这就是以线段树为对象的单点修改,区间求和!
用树状数组应该是最好的,减少了修改时涉及的线段树的个数,保护时空。
数据也是要先离散的,保护时空。
其实主要是保护空间啦,毕竟动态加点的上限只有8e6(再大也开不了),这是远远小于时间上限的。
时间(n+m)*lognlogn。空间也一样。
目前拿了这题bzoj的rank1(当然也是洛谷的)。
#include<bits/stdc++.h>
using namespace std;
#define ch_top 10000000
char ch[ch_top],*now_r=ch;
void read(int &x)
{
while (*now_r<48) ++now_r;
for (x=*now_r-48;*++now_r>=48;)
x=(x<<1)+(x<<3)+*now_r-48;
}
#define N 50010
#define M 20
#define MN 8000000
#define oo 2147483647
struct tree
{
int c[2],s;
}T[MN];int TOT;
struct query
{
int o,l,r,k;
}a[N];
int c[N],last[N];
int *p[N<<1],dy[N<<1],pt;
#define L(x) T[x].c[0]
#define R(x) T[x].c[1]
#define S(x) T[x].s
#define LX T[x].c[0]
#define RX T[x].c[1]
#define mid ((l+r)>>1)
int l,r,w;
void modify(int x,int i)
{
l=1;r=pt;
for (;;)
{
S(x)+=w;
if (l==r) return;
if (i<=mid)
{
if (!LX) LX=++TOT;
x=LX;r=mid;
}
else
{
if (!RX) RX=++TOT;
x=RX;l=mid+1;
}
}
}
int n;
void mo(int i,int x)
{
for (;i<=n;i+=i&-i)
modify(c[i],x);
}
int ql[M][M],qr[M][M];
void init(int *q,int i)
{
for (q[0]=0;i;i-=i&-i)
q[++q[0]]=c[i];
}
int i;
void _move(int *qx,int *qy,bool d)
{
for (i=qy[0]=qx[0];i;--i)
qy[i]=T[qx[i]].c[d];
}
void move(int x,int y,bool d)
{
_move(ql[x],ql[y],d);
_move(qr[x],qr[y],d);
}
int Get;
int _get(int *q,bool d)
{
for (i=1,Get=0;i<=q[0];++i) Get+=S(T[q[i]].c[d]);
return Get;
}
int get(int x,bool d)
{
return _get(qr[x],d)-_get(ql[x],d);
}
int ans;
void order(int x)
{
l=1;r=pt;
for (ans=1;l!=r;)
if (x>mid) {ans+=get(0,0);move(0,0,1);l=mid+1;} else
{move(0,0,0);r=mid;}
}
void find_by_order(int k)
{
l=1;r=pt;
while (l!=r)
{
ans=get(0,0);
if (k>ans) {k-=ans;move(0,0,1);l=mid+1;} else
{move(0,0,0);r=mid;}
}
ans=dy[l];
}
struct ever
{
int l,r;
}e[M];
int top;
void pre(int x)
{
l=1;r=pt;top=0;
while (l!=r)
if (x>mid)
{
e[top]={l,mid};
move(top,top+1,1);++top;
l=mid+1;
}
else
{ move(top,top,0);r=mid; }
while (top--)
if ( get(top,0) )
{
move(top,0,0);
l=e[top].l;r=e[top].r;
while (l!=r)
if ( get(0,1) ) { move(0,0,1);l=mid+1; }
else { move(0,0,0);r=mid; }
ans=dy[l];
return ;
}
ans=-oo;
}
void next(int x)
{
l=1;r=pt;top=0;
while (l!=r)
if (x>mid)
{
move(top,top,1);
l=mid+1;
}
else
{
e[top]={mid+1,r};
move(top,top+1,0);++top;
r=mid;
}
while (top--)
if ( get(top,1) )
{
move(top,0,1);
l=e[top].l;r=e[top].r;
while (l!=r)
if ( get(0,0) ) { move(0,0,0);r=mid; }
else { move(0,0,1);l=mid+1; }
ans=dy[l];
return ;
}
ans=oo;
}
bool xiao(const int *x,const int *y)
{
return *x<*y;
}
int main()
{ //freopen("1.in","r",stdin);freopen("1.out","w",stdout);
ch[fread(ch,1,ch_top,stdin)]=0;
int m,i,o;
read(n);read(m);
for (i=1;i<=n;++i)
{
read(last[i]);
p[i]=last+i;
}
int tot=n;
for (i=1;i<=m;++i)
{
read(a[i].o);read(a[i].l);read(a[i].r);
if (a[i].o==3) p[++tot]=&a[i].r;
else
{
read(a[i].k);
if (a[i].o!=2) p[++tot]=&a[i].k;
}
}
//先离散掉,节约开线段树的时空
sort(p+1,p+tot+1,xiao);
dy[1]=*p[1];*p[1]=pt=1;
for (i=2;i<=tot;++i)
{
if (*p[i]!=dy[pt]) dy[++pt]=*p[i];
*p[i]=pt;
}
w=1;
for (i=1;i<=n;++i) c[i]=i;TOT=n;
for (i=1;i<=n;++i) mo(i,last[i]);
for (o=1;o<=m;++o)
{
if (a[o].o==3)
{
w=-1;i=a[o].l;
mo(i,last[i]);
w=1;
mo(i,last[i]=a[o].r);
} else
{
init(ql[0],a[o].l-1);init(qr[0],a[o].r);
if (a[o].o==1) order(a[o].k); else
if (a[o].o==2) find_by_order(a[o].k); else
if (a[o].o==4) pre(a[o].k);
else next(a[o].k);
printf("%d\n",ans);
}
}
}