二逼平衡树
题目链接:https://www.luogu.org/problemnew/show/P3380
这题解法真是多,各种数据结构的模板练习题啊。。(在此之前下面四个一个都不会)
-
查询k在区间内的排名
-
查询区间内排名为k的值
-
修改某一位值上的数值
-
查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
- 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)
1.线段树套平衡树
对于线段树的每一个节点,开一颗splay,维护序列中的元素
操作1,类似于线段树区间查询,查询在线段树的点的排名的和 nlognlogn
操作2,二分答案 nlognlognlogn
由于每颗splay的形态不一定一样,所以他不能和线段树一样在树上二分
看了别人代码学习来的,直接对数的大小进行二分,满足条件的最大数一定在数列上(刚开始还想着在splay上二分 就复杂多了)
操作3. splay上插入删除 nlognlogn
操作4(5同理).每颗splay上查找找最大值
代码个人感觉如果splay和线段树模板打的熟是要比带修主席树好写的
#include <bits/stdc++.h>
using namespace std;
#define mid (h+t)/2
#define INF 1e9
#define INF2 2147483647
const int maxn=51111;
const int maxn2=2111111;
int a[maxn],count2[maxn2],leftson[maxn2],rightson[maxn2],
fa[maxn2],root[maxn*4],data[maxn2],num,n,m;
bool tt;
struct re
{
int h,t;
}p[maxn*4];
void updata(int x)
{
count2[x]=count2[leftson[x]]+count2[rightson[x]]+1;
}
void rotate(int x,int y)
{
int father=fa[x];
if (y==1)
{
rightson[father]=leftson[x];
if (leftson[x]) fa[leftson[x]]=father;
} else
{
leftson[father]=rightson[x];
if (rightson[x]) fa[rightson[x]]=father;
}
fa[x]=fa[father];
if (fa[father])
{
if (leftson[fa[father]]==father)
leftson[fa[father]]=x;
else rightson[fa[father]]=x;
}
fa[father]=x;
if (y==1) leftson[x]=father; else rightson[x]=father;
updata(father); updata(x);
}
void splay(int x,int goal,int z)
{
if (x==root[z]) return;
int father;
while (fa[x]!=goal)
{
father=fa[x];
if (fa[father]==goal)
{
if (x==leftson[father]) rotate(x,2);
else rotate(x,1);
} else
{
if (father==leftson[fa[father]])
{
if (x==leftson[father])
rotate(father,2),rotate(x,2);
else rotate(x,1),rotate(x,2);
} else
{
if (x==rightson[father])
rotate(father,1),rotate(x,1);
else rotate(x,2),rotate(x,1);
}
}
}
if (goal==0) root[z]=x;
}
void insert2(int x,int y,int z)
{
while (x)
{
count2[x]++;
if (y<data[x])
{
if (!leftson[x]) break;
x=leftson[x];
} else
{
if (!rightson[x]) break;
x=rightson[x];
}
}
data[++num]=y; fa[num]=x; count2[num]=1;
if (x)
{
if (y>=data[x]) rightson[x]=num;
else leftson[x]=num;
}
splay(num,0,z);
}
int query2(int x,int goal)
{
int ans=0;
while (x)
{
if (data[x]==goal) tt=1;
if (data[x]<goal) ans+=count2[leftson[x]]+1,x=rightson[x]; else
x=leftson[x];
}
return(ans);
}
int search(int x,int goal)
{
while (x)
{
if (data[x]==goal) return(x);
if (data[x]<goal) x=rightson[x];
else x=leftson[x];
}
}
void delete2(int number,int goal)
{
splay(goal,0,number);
int x=leftson[goal];
if (x==0)
{
root[number]=rightson[goal];
fa[root[number]]=0; return;
}
while (rightson[x]) x=rightson[x];
splay(x,goal,number);
rightson[x]=rightson[goal];
if (rightson[goal]) fa[rightson[goal]]=x;
updata(x); fa[x]=0; root[number]=x;
}
void build(int x,int h,int t)
{
p[x].h=h; p[x].t=t;
for (int i=h;i<=t;i++)
insert2(root[x],a[i],x);
if (h==t) return;
build(x*2,h,mid); build(x*2+1,mid+1,t);
}
int query(int x,int h,int t,int goal)
{
if (p[x].h>t||p[x].t<h) return(0);
if (h<=p[x].h&&p[x].t<=t)
{
return(query2(root[x],goal));
}
return(query(x*2,h,t,goal)+query(x*2+1,h,t,goal));
}
void delete1(int x,int pos,int goal)
{
int h=p[x].h,t=p[x].t;
int y=search(root[x],goal);
delete2(x,y);
if (h==t) return;
if (pos<=mid) delete1(x*2,pos,goal); else delete1(x*2+1,pos,goal);
}
void insert1(int x,int pos,int goal)
{
int h=p[x].h,t=p[x].t;
insert2(root[x],goal,x);
if (h==t) return;
if (pos<=mid) insert1(x*2,pos,goal); else insert1(x*2+1,pos,goal);
}
int k_th(int x,int y,int k)
{
int h=1,t=INF;
while (h<t)
{
int midd=(h+t+1)/2;
if (1+query(1,x,y,midd)<=k) h=midd; else t=midd-1;
}
return(h);
}
int main()
{
freopen("noip.in","r",stdin);
freopen("noip.out","w",stdout);
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
int c,d,e,f;
for (int i=1;i<=m;i++)
{
cin>>c;
if (c==3) cin>>d>>e; else cin>>d>>e>>f;
if (c==1)
{
cout<<query(1,d,e,f)+1<<endl;
}
if (c==2)
{
cout<<k_th(d,e,f)<<endl;
}
if (c==3)
{
delete1(1,d,a[d]);
insert1(1,d,e);
a[d]=e;
}
if (c==4)
{
int x=query(1,d,e,f)+1;
if (x==1) cout<<-INF2<<endl; else
{
cout<<k_th(d,e,x-1)<<endl;
}
}
if (c==5)
{
tt=0;
int x=query(1,d,e,f)+1;
if (x==e-d+2-tt) cout<<INF2<<endl; else
{
cout<<k_th(d,e,x+tt)<<endl;
}
}
}
return 0;
}
似乎得卡常 洛谷不开o2tle
2.cdq分治(并没有看懂以后再学吧,洛谷上有题解)
3.树状数组套主席树
第一维是树状数组,每个里面开一颗线段树(这个刚开始有点难理解,每个节点里维护的其实并不是任何东西的答案,是为了配合树状数组的使用)
前3个操作就是基本操作吧
对于4/5,计算出这个数的排名+-1再在树上二分就可以了
但是允许元素不出现在原序列就导致5的判断很恶心了
1.查找的时候如果出现在原数列那么是+1不然就是这个排名
2.还有一个坑 就是检验是不是最后一个数时
将这个数搞成离散化的数二分的时候 要注意它可能比最后一个数还要大 所以要特殊处理
*从这个中注意到要注意二分查找的边界限制,显然这个程序中的二分是找到比它小的值,如果这个值比它大那么就要处理了
*待修主席树的核心还是很好写的 然而这道题的细节就很不友善了
说一下这一题代码的实现,离散化是数据结构常有的,核心部分很好写,把查询x的排名变为查询比x小的数有多少个再+1(写平衡树写多了很容易忘记这个)
还有就是空间,(n+m)logn logn 事实我的代码开了100倍,讲道理最坏情况是要500倍左右
#include <bits/stdc++.h>
using namespace std;
#define maxn 100000
#define INF 2147483647
int root[maxn],b[maxn],d[maxn],e[maxn],rea[maxn],cnt,num,n,m,lll;
int tmp1[maxn],tmp2[maxn];
bool tt;
struct re
{
int a,b,c,d;
}a[maxn],c[maxn],f[maxn];
struct ree
{
int x,leftson,rightson;
}p[maxn*50];
bool cmp(re x,re y)
{
if (x.a<y.a) return(true); else return(false);
}
void updata(int x)
{
p[x].x=p[p[x].leftson].x+p[p[x].rightson].x;
}
#define mid (h+t)/2
void change(int &x,int h,int t,int sum,int l)
{
if (!x)
{
x=++cnt;
}
p[x].x+=l;
if (h==t) return;
if (sum<=mid) change(p[x].leftson,h,mid,sum,l);
else change(p[x].rightson,mid+1,t,sum,l);
updata(x);
}
int query2(int x,int h,int t,int num)
{
if (x==0||h>num) return(0);
if (t<num) return(p[x].x);
return(query2(p[x].leftson,h,mid,num)+query2(p[x].rightson,mid+1,t,num));
}
#define lowbit(x) (x&-x)
void insert(int x,int y,int l)
{
while (x<=n)
{
change(root[x],1,lll,y,l);
x+=lowbit(x);
}
}
int query(int x,int y)
{
int ans=0;
while (x)
{
ans+=query2(root[x],1,lll,y);
x-=lowbit(x);
}
return(ans);
}
struct ret
{
int a,c; bool b;
};
int find(int x)
{
int h=1,t=num;
while (h<t)
{
if (f[mid].a<x) h=mid+1; else t=mid;
}
if (f[h].a==x) tt=1;
if (f[h].a<x) return(f[h].d+1);
else return(f[h].d);
}
int k_th(int x,int y,int z)
{
int num1=0,num2=0; x--;
while (x)
{
tmp1[++num1]=root[x]; x-=lowbit(x);
}
while (y)
{
tmp2[++num2]=root[y]; y-=lowbit(y);
}
int h=1,t=lll,tmp;
while (h!=t)
{
tmp=0;
for (int i=1;i<=num1;i++) tmp-=p[p[tmp1[i]].leftson].x;
for (int i=1;i<=num2;i++) tmp+=p[p[tmp2[i]].leftson].x;
if (tmp>=z)
{
t=mid;
for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].leftson;
for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].leftson;
} else
{
h=mid+1;z-=tmp;
for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].rightson;
for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].rightson;
}
}
return (rea[h]);
}
bool check(int x,int y,int goal)
{
int num1=0,num2=0; x--;
while (x)
{
tmp1[++num1]=root[x]; x-=lowbit(x);
}
while (y)
{
tmp2[++num2]=root[y]; y-=lowbit(y);
}
int h=1,t=lll,tmp=0;
while (h!=t)
{
if (goal<=mid)
{
t=mid;
for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].leftson;
for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].leftson;
} else
{
h=mid+1;
for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].rightson;
for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].rightson;
}
}
for (int i=1;i<=num1;i++) tmp-=p[tmp1[i]].x;
for (int i=1;i<=num2;i++) tmp+=p[tmp2[i]].x;
if (tmp) return(1); else return(0);
}
int main()
{
std::ios::sync_with_stdio(false);
freopen("noip.in","r",stdin);
freopen("noip.out","w",stdout);
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i].a,f[i].a=a[i].a,f[i].b=i,f[i].c=0;
num=n;
for (int i=1;i<=m;i++)
{
cin>>b[i];
if (b[i]==1) cin>>c[i].a>>d[i]>>e[i];
if (b[i]==2) cin>>c[i].a>>d[i]>>e[i];
if (b[i]==3)
{
cin>>c[i].a>>d[i];
f[++num].a=d[i]; f[num].b=i;f[num].c=1;
}
if (b[i]==4) cin>>c[i].a>>d[i]>>e[i];
if (b[i]==5) cin>>c[i].a>>d[i]>>e[i];
}
sort(f+1,f+num+1,cmp); f[0].a=-INF;
for (int i=1;i<=num;i++)
{
if (f[i].a!=f[i-1].a) lll++; f[i].d=lll; rea[lll]=f[i].a;
if (f[i].c==0) a[f[i].b].b=lll,a[f[i].b].c=i;
else c[f[i].b].b=lll,c[f[i].b].c=i;
}
//for (int i=1;i<=num;i++) cout<<f[i].a<<endl;
for (int i=1;i<=n;i++)
{
insert(i,a[i].b,1);
}
for (int i=1;i<=m;i++)
{
if (b[i]==1)
{
int x=find(e[i]);
cout<<1+query(d[i],x)-query(c[i].a-1,x)<<endl; //zhuyi0
}
if (b[i]==2)
{
cout<<k_th(c[i].a,d[i],e[i])<<endl;
}
if (b[i]==3)
{
insert(c[i].a,a[c[i].a].b,-1);
insert(c[i].a,c[i].b,1);
a[c[i].a].b=c[i].b;
}
if (b[i]==4)
{
int x=find(e[i]);
int y=query(d[i],x)-query(c[i].a-1,x)+1;
if (y==1) cout<<-INF<<endl;
else cout<<k_th(c[i].a,d[i],y-1)<<endl;
}
if (b[i]==5)
{
tt=0;int x=find(e[i]);
if(tt==1&&check(c[i].a,d[i],x)) tt=1; else tt=0;
int y=query(d[i],x)-query(c[i].a-1,x)+1;
if (y==d[i]-c[i].a+2-tt) cout<<INF<<endl;
else cout<<k_th(c[i].a,d[i],y+tt)<<endl;
}
}
}
4.分块