• UOJ#55. 【WC2014】紫荆花之恋 点分树 替罪羊树 平衡树 splay Treap


    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ55.html

    题解

    做法还是挺容易想到的。

    但是写的话……

    首先这种题如果只要求一棵树中的满足条件的点数(不需要在加点的同时维护答案),那么显然可以点分治:

    假设当前点分中心为 x,设点 y 与 x 的距离为 d[y] ,然后,我们把 $d[a] + d[b] leq r[a] + r[b]$ 移一下项,得到:

    $$d[a]-r[a]leq r[b] - d[b]$$

    那么,我们只需要对于每一个点 b 求出与它不在 x 的同一个子树的节点 a 的个数,a 要满足 $d[a]-r[a]leq r[b]-d[b]$ 。这个东西显然是可以用数据结构维护的。

    具体用什么呢?

    再说。

    现在我们要动态维护答案。考虑如果可以离线怎么做:

    对最终的树建一个点分树,也就是搞一个动态点分治。对于每一个点分中心,维护他控制的区域内所有节点的 d[a]-r[a] ;对于他的每一个子树,也维护一下这个东西。

    那么加入一个点的时候,我们只要在点分树上走一趟,通过维护的信息更新答案的同时加入当前节点的贡献。

    所以我们要的数据结构得支持插入和询问排名,那么自然选用平衡树。

    所以离线的复杂度是 $O(nlog^2 n)$ 。

    然而题目要求强制在线。

    怎么办?

    定期重构点分树?复杂度好像是 $O(nsqrt n log  n)$ 。好像和暴力得分一样……

    定期重构太naive了,我们给他升个级!

    替罪羊树:当点分树的一个子树划分的过度不均匀的时候,重构子树。

    恭喜你得到了一个 $O(nlog^3 n)$ 的算法。

    接下来是写代码和调代码的欢♂乐时光。

    可怕的是,这题还卡常数……

    下面给出两份代码,一个是splay(68331ms的那个),一个是非旋Treap(107878ms)。

    代码1 - splay

    #pragma GCC optimize("Ofast","inline")
    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof (x))
    #define For(i,a,b) for (int i=a;i<=b;i++)
    #define Fod(i,b,a) for (int i=b;i>=a;i--)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
    #define outval(x) printf(#x" = %d
    ",x)
    #define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
    #define outtag(x) puts("----------"#x"----------")
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f|=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=100005,INF=1.05e9,mod=1e9;
    const double checkval=0.80;
    namespace spl{
    	const int S=N*100;
    	int son[S][2],fa[S],val[S],size[S];
    	int cnt=0;
    	namespace cly{
    		int st[S],top=0;
    		inline int new_node(){
    			return top?st[top--]:++cnt;
    		}
    		inline void Recover(int x){
    			st[++top]=x;
    			son[x][0]=son[x][1]=fa[x]=val[x]=size[x]=0;
    		}
    	}
    	using cly::new_node;
    	using cly::Recover;
    	#define ls son[x][0]
    	#define rs son[x][1]
    	int new_node(int v){
    		int x=new_node();
    		val[x]=v;
    		return x;
    	}
    	inline void pushup(int x){
    		size[x]=size[ls]+size[rs]+1;
    	}
    	inline int wson(int x){
    		return son[fa[x]][1]==x;
    	}
    	void rotate(int x){
    		int y=fa[x],z=fa[y],L=son[y][1]==x,R=L^1;
    		if (z)
    			son[z][son[z][1]==y]=x;
    		fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
    		son[y][L]=son[x][R],son[x][R]=y;
    		pushup(y),pushup(x);
    	}
    	void splay(int x){
    		for (int y=fa[x];fa[x];rotate(x),y=fa[x])
    			if (fa[y])
    				rotate(wson(x)==wson(y)?y:x);
    	}
    	inline void Ins(int &a,int v){
    		register int x=a,c=0;
    		int f=0;
    		while (x){
    			c++;
    			size[x]++;
    			f=x;
    			x=v<val[x]?ls:rs;
    		}
    		val[son[f][v>=val[f]]=x=new_node()]=v,fa[x]=f,size[x]=1;
    		if (!a||c>30)
    			splay(a=x);
    	}
    	int Query(int &rt,int v){
    		int ans=0,c=0;
    		register int x=rt,p=0;
    		while (x){
    			p=x,c++;
    			if (val[x]<=v)
    				ans+=size[ls]+1,x=rs;
    			else
    				x=ls;
    		}
    		if (p&&c>30)
    			splay(rt=p);
    		return ans;
    	}
    	void Remove(int x){
    		if (x)
    			Remove(ls),Remove(rs),Recover(x);
    	}
    	int id[N];
    	void Build(int &x,int L,int R,int f){
    		if (L>R)
    			return;
    		int mid=(L+R)>>1;
    		fa[x=id[mid]]=f;
    		Build(ls,L,mid-1,x);
    		Build(rs,mid+1,R,x);
    		pushup(x);
    	}
    	void Build(int &x,vector <int> &v){
    		if (v.empty())
    			return;
    		int n=0;
    		sort(v.begin(),v.end());
    		for (auto i : v)
    			id[++n]=new_node(i);
    		Build(x,1,n,0);
    	}
    	#undef ls
    	#undef rs
    }
    int Test,n;
    LL ans=0;
    vector <int> e[N];
    int fa[N][20],depth[N],len[N];
    int LCA(int x,int y){
    	if (depth[x]<depth[y])
    		swap(x,y);
    	Fod(i,16,0)
    		if (depth[x]-(1<<i)>=depth[y])
    			x=fa[x][i];
    	if (x==y)
    		return x;
    	Fod(i,16,0)
    		if (fa[x][i]!=fa[y][i])
    			x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    int Dis(int x,int y){
    	return len[x]+len[y]-2*len[LCA(x,y)];
    }
    int r[N];
    namespace dt{
    	int fa[N],size[N],mxs[N],depth[N];
    	int rt[N],rtf[N];
    	int node_cnt=0;
    	void Init(){
    		clr(fa),clr(size),clr(mxs),clr(depth),clr(rt),clr(rtf);
    		depth[0]=-1,node_cnt++;
    		size[1]=1,mxs[1]=depth[1]=rt[1]=0;
    		spl::Ins(rt[1],0-r[1]);
    	}
    	int id[N],idc=0;
    	void dfs(int x,int pre,int d){
    		id[++idc]=x;
    		for (auto y : e[x])
    			if (y!=pre&&depth[y]>=d)
    				dfs(y,x,d);
    	}
    	int sz[N],msz[N],RT,Size;
    	void dfs2(int x,int pre){
    		sz[x]=1,msz[x]=0;
    		for (auto y : e[x])
    			if (y!=pre&&!size[y]){
    				dfs2(y,x);
    				sz[x]+=sz[y];
    				msz[x]=max(msz[x],sz[y]);
    			}
    		msz[x]=max(msz[x],Size-sz[x]);
    		if (!RT||msz[x]<msz[RT])
    			RT=x;
    	}
    	vector <int> id1,id2;
    	void dfs3(int x,int pre,int anc){
    		sz[x]=1;
    		id1.pb(Dis(x,anc)-r[x]);
    		if (fa[anc])
    			id2.pb(Dis(x,fa[anc])-r[x]);
    		for (auto y : e[x])
    			if (y!=pre&&!size[y])
    				dfs3(y,x,anc),sz[x]+=sz[y];
    	}
    	void build(int x,int f,int n){
    		RT=0,Size=n;
    		dfs2(x,0);
    		x=RT;
    		fa[x]=f,depth[x]=depth[f]+1,size[x]=n,mxs[x]=msz[x];
    		id1.clear(),id2.clear();
    		dfs3(x,0,x);
    		spl::Build(rt[x],id1);
    		spl::Build(rtf[x],id2);
    		for (auto y : e[x])
    			if (!size[y])
    				build(y,x,sz[y]);
    	}
    	void Rebuild(int x,int f){
    		idc=0;
    		dfs(x,0,depth[x]);
    		For(_t,1,idc){
    			int i=id[_t];
    			spl::Remove(rt[i]),spl::Remove(rtf[i]);
    			depth[i]=size[i]=fa[i]=mxs[i]=rt[i]=rtf[i]=0;
    		}
    		build(x,f,idc);
    	}
    	void Ins(int x,int f){
    		static vector <int> v;
    		fa[x]=f,depth[x]=depth[f]+1;
    		rt[x]=rtf[x]=0;
    		v.clear();
    		for (int i=x;i;i=fa[i]){
    			v.pb(i),size[i]++;
    			mxs[fa[i]]=max(mxs[fa[i]],size[i]);
    			spl::Ins(rt[i],Dis(i,x)-r[x]);
    			if (fa[i])
    				spl::Ins(rtf[i],Dis(fa[i],x)-r[x]);
    		}
    		node_cnt++;
    		reverse(v.begin(),v.end());
    		for (auto i : v)
    			if (mxs[i]>checkval*size[i]){
    				Rebuild(i,fa[i]);
    				break;
    			}
    		if (size[x]>1)
    			ans+=spl::Query(rt[x],r[x])-1;
    		for (int i=x,f,d;depth[i];i=f){
    			f=fa[i],d=Dis(f,x);
    			ans+=spl::Query(rt[f],r[x]-d)-spl::Query(rtf[i],r[x]-d);
    		}
    	}
    }
    int main(){
    	Test=read(),n=read();
    	For(i,1,n){
    		int f=read()^(ans%mod),c=read();
    		r[i]=read();
    		if (!f){
    			dt::Init();
    			printf("%lld
    ",ans);
    			continue;
    		}
    		assert(1<=f&&f<i);
    		e[i].pb(f),e[f].pb(i);
    		fa[i][0]=f;
    		For(j,1,16)
    			fa[i][j]=fa[fa[i][j-1]][j-1];
    		depth[i]=depth[f]+1;
    		len[i]=len[f]+c;
    		dt::Ins(i,f);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

      

    代码2 - Treap(非旋)

    #pragma GCC optimize("Ofast","inline")
    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof (x))
    #define For(i,a,b) for (int i=a;i<=b;i++)
    #define Fod(i,b,a) for (int i=b;i>=a;i--)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
    #define outval(x) printf(#x" = %d
    ",x)
    #define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
    #define outtag(x) puts("----------"#x"----------")
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f|=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=100005,INF=1.05e9,mod=1e9;
    const double checkval=0.80;
    int randint(){
    	#ifdef windows
    		return (rand()<<15)^rand();
    	#else
    		return rand();
    	#endif
    }
    namespace Treap{
    	const int S=N*100;
    	int son[S][2],val[S],ckv[S],size[S];
    	int cnt=0;
    	namespace cly{
    		int st[S],top=0;
    		inline int new_node(){
    			return top?st[top--]:++cnt;
    		}
    		inline void Recover(int x){
    			st[++top]=x,son[x][0]=son[x][1]=val[x]=size[x]=0;
    		}
    	}
    	#define ls son[x][0]
    	#define rs son[x][1]
    	int new_node(int v){
    		int x=cly::new_node();
    		val[x]=v,ckv[x]=randint(),size[x]=1;
    		return x;
    	}
    	void pushup(int x){
    		size[x]=size[ls]+size[rs]+1;
    	}
    	int Merge(int x,int y){
    		if (!x||!y)
    			return x+y;
    		if (ckv[x]<ckv[y]){
    			son[x][1]=Merge(son[x][1],y),pushup(x);
    			return x;
    		}
    		else {
    			son[y][0]=Merge(x,son[y][0]),pushup(y);
    			return y;
    		}
    	}
    	pair <int,int> Split(int x,int k){
    		if (!x)
    			return mp(0,0);
    		if (k<=size[ls]){
    			pair <int,int> p=Split(ls,k);
    			ls=p.se,pushup(x);
    			return mp(p.fi,x);
    		}
    		else {
    			pair <int,int> p=Split(rs,k-size[ls]-1);
    			rs=p.fi,pushup(x);
    			return mp(x,p.se);
    		}
    	}
    	int _Rank(int x,int v){
    		return x?(val[x]>v?_Rank(ls,v):size[ls]+1+_Rank(rs,v)):0;
    	}
    	int Rank(int x,int v){
    		return _Rank(x,v-1);
    	}
    	int Query(int x,int v){
    		return _Rank(x,v);
    	}
    	int id[N];
    	int tL[N],tR[N],lcnt,rcnt;
    	void Build(int &x,int L,int R){
    		if (L>R)
    			return;
    		int Mi=L;
    		For(i,L+1,R)
    			if (ckv[id[i]]<ckv[id[Mi]])
    				Mi=i;
    		swap(id[Mi],id[R]);
    		x=id[R];
    		lcnt=rcnt=0;
    		For(i,L,R-1)
    			if (val[id[i]]<=val[x])
    				tL[++lcnt]=id[i];
    			else
    				tR[++rcnt]=id[i];
    		int c=L-1;
    		For(i,1,lcnt)
    			id[++c]=tL[i];
    		int mid=c;
    		For(i,1,rcnt)
    			id[++c]=tR[i];
    		Build(ls,L,mid);
    		Build(rs,mid+1,R-1);
    		pushup(x);
    	}
    	void Build(int &x,vector <int> &v){
    		if (v.empty())
    			return;
    		int n=0;
    		for (auto i : v)
    			id[++n]=new_node(i);
    		Build(x,1,n);
    	}
    	void Ins(int &x,int v){
    		pair <int,int> p=Split(x,_Rank(x,v));
    		x=Merge(p.fi,Merge(new_node(v),p.se));
    	}
    	void Remove(int x){
    		if (x)
    			Remove(ls),Remove(rs),cly::Recover(x);
    	}
    	#undef ls
    	#undef rs
    }
    int Test,n;
    LL ans=0;
    vector <int> e[N];
    int fa[N][20],depth[N],len[N];
    int LCA(int x,int y){
    	if (depth[x]<depth[y])
    		swap(x,y);
    	Fod(i,16,0)
    		if (depth[x]-(1<<i)>=depth[y])
    			x=fa[x][i];
    	if (x==y)
    		return x;
    	Fod(i,16,0)
    		if (fa[x][i]!=fa[y][i])
    			x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    int Dis(int x,int y){
    	return len[x]+len[y]-2*len[LCA(x,y)];
    }
    int r[N];
    namespace dt{
    	int fa[N],size[N],mxs[N],depth[N];
    	int rt[N],rtf[N];
    	int node_cnt=0;
    	void Init(){
    		clr(fa),clr(size),clr(mxs),clr(depth),clr(rt),clr(rtf);
    		depth[0]=-1,node_cnt++;
    		size[1]=1,mxs[1]=depth[1]=rt[1]=0;
    		Treap::Ins(rt[1],0-r[1]);
    	}
    	int id[N],idc=0;
    	void dfs(int x,int pre,int d){
    		id[++idc]=x;
    		for (auto y : e[x])
    			if (y!=pre&&depth[y]>=d)
    				dfs(y,x,d);
    	}
    	int sz[N],msz[N],RT,Size;
    	void dfs2(int x,int pre){
    		sz[x]=1,msz[x]=0;
    		for (auto y : e[x])
    			if (y!=pre&&!size[y]){
    				dfs2(y,x);
    				sz[x]+=sz[y];
    				msz[x]=max(msz[x],sz[y]);
    			}
    		msz[x]=max(msz[x],Size-sz[x]);
    		if (!RT||msz[x]<msz[RT])
    			RT=x;
    	}
    	vector <int> id1,id2;
    	void dfs3(int x,int pre,int anc){
    		sz[x]=1;
    		id1.pb(Dis(x,anc)-r[x]);
    		if (fa[anc])
    			id2.pb(Dis(x,fa[anc])-r[x]);
    		for (auto y : e[x])
    			if (y!=pre&&!size[y])
    				dfs3(y,x,anc),sz[x]+=sz[y];
    	}
    	void build(int x,int f,int n){
    		RT=0,Size=n;
    		dfs2(x,0);
    		x=RT;
    		fa[x]=f,depth[x]=depth[f]+1,size[x]=n,mxs[x]=msz[x];
    		id1.clear(),id2.clear();
    		dfs3(x,0,x);
    		Treap::Build(rt[x],id1);
    		Treap::Build(rtf[x],id2);
    		for (auto y : e[x])
    			if (!size[y])
    				build(y,x,sz[y]);
    	}
    	void Rebuild(int x,int f){
    		idc=0;
    		dfs(x,0,depth[x]);
    		For(_t,1,idc){
    			int i=id[_t];
    			Treap::Remove(rt[i]),Treap::Remove(rtf[i]);
    			depth[i]=size[i]=fa[i]=mxs[i]=rt[i]=rtf[i]=0;
    		}
    		build(x,f,idc);
    	}
    	void Ins(int x,int f){
    		static vector <int> v;
    		fa[x]=f,depth[x]=depth[f]+1;
    		rt[x]=rtf[x]=0;
    		v.clear();
    		for (int i=x;i;i=fa[i]){
    			v.pb(i),size[i]++;
    			mxs[fa[i]]=max(mxs[fa[i]],size[i]);
    			Treap::Ins(rt[i],Dis(i,x)-r[x]);
    			if (fa[i])
    				Treap::Ins(rtf[i],Dis(fa[i],x)-r[x]);
    		}
    		node_cnt++;
    		reverse(v.begin(),v.end());
    		for (auto i : v)
    			if (mxs[i]>checkval*size[i]){
    				Rebuild(i,fa[i]);
    				break;
    			}
    		if (size[x]>1)
    			ans+=Treap::Query(rt[x],r[x])-1;
    		for (int i=x,f,d;depth[i];i=f){
    			f=fa[i],d=Dis(f,x);
    			ans+=Treap::Query(rt[f],r[x]-d)-Treap::Query(rtf[i],r[x]-d);
    		}
    	}
    }
    int main(){
    	srand(_SEED_);
    	Test=read(),n=read();
    	For(i,1,n){
    		int f=read()^(ans%mod),c=read();
    		r[i]=read();
    		if (!f){
    			dt::Init();
    			printf("%lld
    ",ans);
    			continue;
    		}
    		e[i].pb(f),e[f].pb(i);
    		fa[i][0]=f;
    		For(j,1,16)
    			fa[i][j]=fa[fa[i][j-1]][j-1];
    		depth[i]=depth[f]+1;
    		len[i]=len[f]+c;
    		dt::Ins(i,f);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    git 创建一个空分支
    github page的两种类型
    hexo-theme-next
    github网页
    Linux下的CPU使用率与服务器负载的关系与区别
    mysql数据库优化日志(更)-howyue
    图片延时加载
    jQuery实现页面滚动时顶部动态显示隐藏
    TCP与UDP区别
    记一次网站服务器迁移(my)
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ55.html
Copyright © 2020-2023  润新知