https://www.luogu.org/problemnew/show/P3380
这题我是用线段树+splay写的,第一次写树套树,一开始感觉是个很难想象很难写的事情,但说白了这一题就是线段树的每个区间套一个splay。。。
然后对于第二个操作就是二分答案val,验证rank值。。。
第一个版本写完无限TLE...
改了一个晚上终于改到了洛谷5000多ms,bzoj7200ms。。。写此文只是纪念一下我第一个树套树。。。
结合别人代码后有几点小优化:
1、发现对于第二个二分查询,找前继后缀的时候,不能每次都splay,单独写一个后,bzoj上快了1300ms
2、还是对于第二个操作,可以开一个vector存下每个需要查询的区间段的rt,二分时直接for就行,不用每次都查询哪几个区间,bzoj上快了600ms
3、对于delete元素的操作,采用只查找前继pre,然后此时需要delete的数x在root上,若cnt>1就直接减一个退出,若否,则splay前继pre到root,oldrt为x对应的下标,即上一个根节点,然后ch[pre][1]=ch[oldrt][1];par[ch[oldrt][1]]=pre;因为把前继splay,不会影响到oldrt的右半部分的节点,而且那时的前继也肯定是没有右儿子的,不然就不是前继了,所以当前继splay上来后,只需上面的操作即可。也可以快一点。。。
4、刚开始插入完毕后应该是一条链,如果这个时候开始第二个操作就会变成O(n)的了,可以先预处理splay一下,但是不好确定哪些点是该rt的(未实现想法)。。。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define FI first
#define SE second
#define MP make_pair
#define PI pair<int,int>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define test printf("here!!!\n")
using namespace std;
const int mx=5e4+10;
const int MAX=4e6+10;
int n,m;
int nl,nr,pos;
vector<int>vec;
struct Node
{
int l,r,root;
}t[mx<<2|2];
int ch[MAX][2],cnt[MAX],sz[MAX],val[MAX],par[MAX],tot;
int a[mx];
int res;
int get(int x)
{
return ch[par[x]][1]==x;
}
void pushup(int x)
{
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
}
void rot(int x)
{
int y=par[x],z=par[y],k=get(x),w=get(y);
ch[z][w]=x;
par[x]=z;
ch[y][k]=ch[x][k^1];
par[ch[x][k^1]]=y;
ch[x][k^1]=y;
par[y]=x;
pushup(y),pushup(x);
}
void splay(int rt,int x,int goal)
{
while (par[x]!=goal)
{
int y=par[x],z=par[y];
if (z!=goal)
{
get(x)==get(y)?rot(y):rot(x);
}
rot(x);
}
if (goal==0) t[rt].root=x;
}
void ins(int rt,int x)
{
int r=t[rt].root,fa=0;
while (val[r]!=x&&r)
{
fa=r;
r=ch[r][x>val[r]];
}
if (r) ++cnt[r];
else
{
r=++tot;
if (fa) ch[fa][x>val[fa]]=r;
ch[r][0]=ch[r][1]=0;
par[r]=fa,val[r]=x;
sz[r]=cnt[r]=1;
}
splay(rt,r,0);
}
int rrank(int rt,int x)
{
int r=t[rt].root,hgf=0;
while (r)
{
if (val[r]==x) return hgf+(ch[r][0]?sz[ch[r][0]]:0);
else if (val[r]<x)
{
hgf+=(ch[r][0]?sz[ch[r][0]]:0)+cnt[r];
r=ch[r][1];
}
else r=ch[r][0];
}
return hgf;
}
int rrfind(int rt,int x)
{
int r=t[rt].root,hgf=0;
while (r)
{
if (val[r]==x) return hgf+(ch[r][0]?sz[ch[r][0]]:0)+cnt[r];
else if (val[r]<x)
{
hgf+=(ch[r][0]?sz[ch[r][0]]:0)+cnt[r];
r=ch[r][1];
}
else r=ch[r][0];
}
return hgf;
}
void findxb(int rt,int x)
{
if (!t[rt].root) return;
int r=t[rt].root;
while (val[r]!=x&&ch[r][x>val[r]])
{
r=ch[r][x>val[r]];
}
splay(rt,r,0);
}
int presucc(int rt,int x,int k)
{
findxb(rt,x);
int r=t[rt].root;
if ((val[r]<x&&!k)||(x<val[r]&&k)) return r;
r=ch[r][k];
int fk=k^1;
while (ch[r][fk]) r=ch[r][fk];
return r;
}
int splaypre(int rt,int x)
{
int ans=-2147483647;
int r=t[rt].root;
while (r)
{
if (val[r]<x)
{
if (ans<val[r]) ans=val[r];
r=ch[r][1];
}
else r=ch[r][0];
}
return ans;
}
int splaysucc(int rt,int x)
{
int ans=2147483647;
int r=t[rt].root;
while (r)
{
if (val[r]>x)
{
if (ans>val[r]) ans=val[r];
r=ch[r][0];
}
else r=ch[r][1];
}
return ans;
}
void del(int rt,int x)
{
int pre=presucc(rt,x,0);
int r=t[rt].root;
if (val[r]==x)
{
if (cnt[r]>1)
{
--cnt[r];
pushup(r);
return;
}
int oldrt=t[rt].root;
splay(rt,pre,0);
ch[pre][1]=ch[oldrt][1];par[ch[oldrt][1]]=pre;
pushup(pre);
}
}
void build(int rt,int l,int r)
{
ins(rt,-2147483647);
for (int i=l;i<=r;++i)
{
ins(rt,a[i]);
}
ins(rt,2147483647);
t[rt].l=l,t[rt].r=r;
if (l==r) return;
int mid=(l+r)>>1;
build(lson);
build(rson);
}
void segvec(int rt)
{
if (nl<=t[rt].l&&t[rt].r<=nr)
{
vec.pb(rt);
return;
}
int mid=(t[rt].l+t[rt].r)>>1;
if (nl<=mid) segvec(rt<<1);
if (nr>mid) segvec(rt<<1|1);
}
int segrfind(int rt,int c)
{
if (nl<=t[rt].l&&t[rt].r<=nr)
{
return rrank(rt,c)-1;
}
int mid=(t[rt].l+t[rt].r)>>1;
int ans=0;
if (nl<=mid) ans+=segrfind(rt<<1,c);
if (nr>mid) ans+=segrfind(rt<<1|1,c);
return ans;
}
int segkth(int rt,int k)
{
segvec(1);
int ls=0;
int rs=1e8;
while (ls<=rs)
{
int mid=(ls+rs)>>1;
int rk=0;
for (int i=vec.size()-1;i>=0;--i)
{
rk+=rrfind(vec[i],mid)-1;
}
if (rk<k)
{
ls=mid+1;
}
else
{
rs=mid-1;
}
}
return ls;
}
void segupdata(int rt,int c)
{
del(rt,a[pos]),ins(rt,c);
if (t[rt].l==t[rt].r) return;
int mid=(t[rt].l+t[rt].r)>>1;
if (pos<=mid) segupdata(rt<<1,c);
else segupdata(rt<<1|1,c);
}
void segpresucc(int rt,int c,int k)
{
if (nl<=t[rt].l&&t[rt].r<=nr)
{
if (!k) res=max(res,splaypre(rt,c));
else res=min(res,splaysucc(rt,c));
return;
}
int mid=(t[rt].l+t[rt].r)>>1;
if (nl<=mid) segpresucc(rt<<1,c,k);
if (nr>mid) segpresucc(rt<<1|1,c,k);
}
int main()
{
int op,k;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
}
build(1,1,n);
for (int i=1;i<=m;++i)
{
scanf("%d",&op);
if (op!=3) scanf("%d%d%d",&nl,&nr,&k);
else scanf("%d%d",&pos,&k);
if (op==1)
{
printf("%d\n",segrfind(1,k)+1);
}
else if (op==2)
{
vec.clear();
printf("%d\n",segkth(1,k));
}
else if (op==3)
{
segupdata(1,k);
a[pos]=k;
}
else if (op==4)
{
res=-2147483647;
segpresucc(1,k,0);
printf("%d\n",res);
}
else
{
res=2147483647;
segpresucc(1,k,1);
printf("%d\n",res);
}
}
}
298行代码(虽然括号占了很多行2333),差一点超300了。。。
然后我用vector独立给每个区间段开点后终于到了346行,也完美的TLE了。。。
想想应该也会TLE的,平衡树本来下标之间就很跳跃,在整个大数组里跳跃和在vector里跳跃应该没什么大区别。。。
(所以我觉得图论里的vector存边比前式链向星存边快一点。。。