zoukankan      html  css  js  c++  java
  • 【YbtOJ#652】集合比较

    题目

    题目链接: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;
    }
    
  • 相关阅读:
    ndarray数据类型
    创建ndarray
    SqlHelper
    插入订单并且输出订单号的sql存储过程
    JQury自动切换图片
    设计模式--责任链模式
    设计模式--模板方法模式
    设计模式--观察者模式
    设计模式--享元模式
    设计模式--组合模式
  • 原文地址:https://www.cnblogs.com/stoorz/p/14416966.html
Copyright © 2011-2022 走看看