题目
题目链接:https://www.ybtoj.com.cn/contest/118/problem/1
(nleq 50000)。
思路
很容易想到平衡树。但是我们在插入 (x) 时,需要知道 (a_x,b_x) 的排名,以及平衡树上当前节点的 (a,b) 排名,这样才可以更好比较。
这启发我们给平衡树中每一个点一个权值表示排名,需要保证按照中序遍历整棵平衡树后权值严格递增。
所以我们可以给每一个点一个排名区间 ([l,r]),取权值 (mid=frac{l+r}{2}),然后左子节点的排名区间为 ([l,mid]),右子树为 ([mid,r])。可以使用 double,精度与平衡树深度正相关。
但是这样当我们旋转平衡树的时候需要重构整棵子树的权值,所以我们必须采用重量平衡树来保证复杂度。我采用的是 Treap。每次旋转完之后直接暴力重构子树区间即可。
为什么时间复杂度是正确的呢?陈立杰的论文《重量平衡树和后缀平衡树在信息学奥赛中的应用》中给出了简洁的证明:
注意当两个点排名相等时需要进行一些操作将两个点合并。直接把第二个点打个标记到第一个点即可。
时间复杂度 (O(nlog n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=50010;
const double eps=1e-8,Inf=1e12;
int n,rt,a[N],b[N],num[N];
double id[N];
struct Treap
{
int tot,val[N],cnt[N],siz[N],dat[N],lc[N],rc[N];
double rk[N][2];
int check(int x,int y)
{
if (fabs(id[a[x]]-id[a[y]])>eps) return id[a[x]]<id[a[y]];
if (fabs(id[b[x]]-id[b[y]])>eps) return id[b[x]]<id[b[y]];
return 2;
}
void pushup(int x)
{
siz[x]=siz[lc[x]]+siz[rc[x]]+cnt[x];
}
void pushdown(int x,double l,double r)
{
double mid=(l+r)/2.0;
rk[x][0]=l; rk[x][1]=r; id[val[x]]=mid;
if (lc[x]) pushdown(lc[x],l,mid);
if (rc[x]) pushdown(rc[x],mid,r);
}
int New(int v,double l,double r)
{
int p=++tot;
val[p]=v; cnt[p]=siz[p]=1; dat[p]=rand();
rk[p][0]=l; rk[p][1]=r; id[v]=(l+r)/2.0;
return p;
}
void build()
{
rt=New(0,0,Inf); rc[rt]=New(n+1,Inf/2.0,Inf);
pushup(rt);
}
void zig(int &x)
{
int y=lc[x],c=rc[y];
double l=rk[x][0],r=rk[x][1];
lc[x]=c; rc[y]=x; x=y;
pushup(rc[x]); pushup(x);
pushdown(x,l,r);
}
void zag(int &x)
{
int y=rc[x],c=lc[y];
double l=rk[x][0],r=rk[x][1];
rc[x]=c; lc[y]=x; x=y;
pushup(lc[x]); pushup(x);
pushdown(x,l,r);
}
int ins(int x,int k,double l,double r)
{
if (!x) { x=New(k,l,r); return x; }
double mid=(l+r)/2.0;
int f=check(k,val[x]);
if (f==2) cnt[x]++,num[k]=val[x];
if (f==1)
{
lc[x]=ins(lc[x],k,l,mid);
if (dat[lc[x]]>dat[x]) zig(x);
}
if (f==0)
{
rc[x]=ins(rc[x],k,mid,r);
if (dat[rc[x]]>dat[x]) zag(x);
}
pushup(x);
return x;
}
int query(int x,int k)
{
int f=check(k,val[x]);
if (f==2) return siz[lc[x]]+cnt[x];
if (f==1) return query(lc[x],k);
if (f==0) return query(rc[x],k)+siz[lc[x]]+cnt[x];
}
}treap;
int main()
{
freopen("comparison.in","r",stdin);
freopen("comparison.out","w",stdout);
srand(1023);
scanf("%d",&n);
treap.build();
a[n+1]=b[n+1]=num[n+1]=n+1;
for (int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
a[i]=num[a[i]]; b[i]=num[b[i]]; num[i]=i;
rt=treap.ins(rt,i,0,Inf);
printf("%d
",treap.query(rt,i));
}
return 0;
}