YbtOJ#652-集合比较【Treap】
正题
题目链接:http://www.ybtoj.com.cn/problem/652
题目大意
定义一个元素为一个有序集合包含两个元素\(C=\{A,B\}\)
集合\(C=\{A,B\}\)的大小以\(A\)为第一关键字,\(B\)为第二关键字比较大小。
开始有两个元素\(S=\{S,S\},T=\{T,T\}\)且\(S<T\)。
然后\(n\)次加入一个新的由两个之前的元素依次组成的新元素,求出现过的元素小于等于它的有多少个。
\(1\leq n\leq 5\times 10^4\)
解题思路
如果递归比较是\(O(n)\)的显然不行,但是我们比较新的元素和旧的元素大小的时候如果我们可以知道以前元素的大小关系就可以快速比较(因为新的元素由旧的元素组成)
所以相当于我们要动态维护大小关系,因为要插入好像只能用平衡树。
然后要在树上查询两个点的大小关系,因为你要在平衡树上边移动边查询,所以不能用查询的时候结构会改变的平衡树(\(Splay\)之类的),正解是替罪羊的,反正这里用了\(Treap\)。
比较大小的时候直接在\(Treap\)上暴力跳找\(LCA\)就好了,深度是\(log\)级别的,然后不能动态维护深度所以有点麻烦
时间复杂度\(O(n\log^2 n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=51000;
int n,qfn,tot,a[N],b[N],p[N],v[N];
int t[N][2],siz[N],cnt[N],dat[N],fa[N];
int cmp(int x,int y){
++qfn;
if(x==y)return 2;
while(x){
if(fa[x]==y)
return t[y][0]==x;
v[x]=qfn;
x=fa[x];
}
while(y){
if(v[fa[y]]==qfn)
return t[fa[y]][1]==y;
y=fa[y];
}
return 0;
}
int cap(int x,int y){
int tmp=cmp(a[x],a[y]);
if(tmp!=2)return tmp;
return cmp(b[x],b[y]);
}
void PushUp(int x){
siz[x]=siz[t[x][0]]+siz[t[x][1]]+cnt[x];
return;
}
void zig(int &x){
int y=t[x][0];
t[x][0]=t[y][1];fa[t[y][1]]=x;
t[y][1]=x;fa[y]=fa[x];fa[x]=y;x=y;
PushUp(t[x][1]);PushUp(x);return;
}
void zag(int &x){
int y=t[x][1];
t[x][1]=t[y][0];fa[t[y][0]]=x;
t[y][0]=x;fa[y]=fa[x];fa[x]=y;x=y;
PushUp(t[x][0]);PushUp(x);return;
}
void Insert(int &x,int pos){
if(!x){
x=++tot;p[pos]=x;
a[tot]=a[pos];
b[tot]=b[pos];
cnt[x]=siz[x]=1;
dat[x]=rand();return;
}
int tmp=cap(pos,x),sum=0;
if(tmp==2){
p[pos]=x;cnt[x]++;
PushUp(x);return;
}
else if(tmp){
Insert(t[x][0],pos);fa[t[x][0]]=x;
if(dat[t[x][0]]>dat[x])zig(x);
}
else{
Insert(t[x][1],pos);fa[t[x][1]]=x;
if(dat[t[x][1]]>dat[x])zag(x);
}
PushUp(x);return;
}
int Query(int x){
int ans=siz[t[x][0]]+cnt[x];
while(x){
if(t[fa[x]][0]!=x)
ans+=siz[t[fa[x]][0]]+cnt[fa[x]];
x=fa[x];
}
return ans;
}
int main()
{
// freopen("comparison.in","r",stdin);
// freopen("comparison.out","w",stdout);
srand(19260817);
scanf("%d",&n);n++;
t[1][1]=n+1;
p[1]=1;a[1]=b[1]=1;
p[n+1]=n+1;a[n+1]=b[n+1]=n+1;
cnt[1]=cnt[n+1]=1;
fa[n+1]=tot=1;
PushUp(n+1);PushUp(1);
int rt=1;
for(int i=2;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);
a[i]++;b[i]++;
a[i]=p[a[i]];b[i]=p[b[i]];
Insert(rt,i);
printf("%d\n",Query(p[i]));
}
return 0;
}