第一次打“真正的”动态点分

如果树是静态的,直接点分:用$d_x$代表$x$到分治中心的距离,限制条件即为$d_i+d_j\leq r_i+r_j$,考虑枚举$j$,那么我们要查询有多少满足$d_i-r_i\leq r_j-d_j$的$i$,用平衡树维护即可

现在树是动态的,那么每次我们往点分树中加一个叶子,先更新答案再更新平衡树即可,每个点分树中的点存两棵平衡树,一棵存以这个点为dfs起点的$d_i-r_i$,另一棵存以(它父亲到它管辖范围的第一个点)为dfs起点的$d_i-r_i$,查询时容斥一下就可以了

但直接加叶子会造成点分树不平衡,所以这里用替罪羊树的思想重构,这样就能保证时间复杂度

实现起来还是需要一点技巧的,如果要重构点分树中的一个点$x$,那么在原树中dfs时只访问那些在点分树中比$x$深的点就对应着$x$的点分树子树了,平衡树使用旋转treap,重构时先内存回收,再排序后$O(n)$建treap,这样会快一些(好吧主要是我写的常数太大...)

#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<assert.h>
using namespace std;
typedef long long ll;
const int inf=2147483647;
ll ans;
int r[100010],*d,*r1,*r2,*sz,*df;
namespace tree{
	int h[100010],nex[200010],to[200010],v[200010],M;
	void ins(int a,int b,int c){
		M++;
		to[M]=b;
		v[M]=c;
		nex[M]=h[a];
		h[a]=M;
	}
	int fa[100010][17],dep[100010],dis[100010];
	void add(int a,int b,int c){
		ins(a,b,c);
		ins(b,a,c);
		fa[b][0]=a;
		dep[b]=dep[a]+1;
		dis[b]=dis[a]+c;
		for(int i=1;i<17;i++)fa[b][i]=fa[fa[b][i-1]][i-1];
	}
	int lca(int x,int y){
		int i;
		if(dep[x]<dep[y])swap(x,y);
		for(i=16;i>=0;i--){
			if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
		}
		if(x==y)return x;
		for(i=16;i>=0;i--){
			if(fa[x][i]!=fa[y][i]){
				x=fa[x][i];
				y=fa[y][i];
			}
		}
		return fa[x][0];
	}
	int vis[100010],siz[100010],lim,C;
	#define ok vis[to[i]]!=C&&to[i]!=fa&&d[to[i]]>=lim
	void dfs1(int fa,int x){
		siz[x]=1;
		for(int i=h[x];i;i=nex[i]){
			if(ok){
				dfs1(x,to[i]);
				siz[x]+=siz[to[i]];
			}
		}
	}
	int mn,cn,n;
	void dfs2(int fa,int x){
		int i,k=0;
		for(i=h[x];i;i=nex[i]){
			if(ok){
				dfs2(x,to[i]);
				k=max(k,siz[to[i]]);
			}
		}
		k=max(k,n-siz[x]);
		if(k<mn){
			mn=k;
			cn=x;
		}
	}
}
int getdis(int x,int y){
	using namespace tree;
	return dis[x]+dis[y]-dis[lca(x,y)]*2;
}
namespace treap{
	int fa[4000010],ch[4000010][2],fix[4000010],s[4000010],v[4000010],st[4000010],tp,M;
	#define l(x) ch[x][0]
	#define r(x) ch[x][1]
	int node(int d){
		int x;
		if(tp){
			x=st[tp--];
			fa[x]=l(x)=r(x)=0;
		}else
			x=++M;
		fix[x]=rand();
		v[x]=d;
		s[x]=1;
		return x;
	}
	void pushup(int x){
		s[x]=s[l(x)]+s[r(x)]+1;
	}
	void rot(int x){
		int y,z,f,b;
		y=fa[x];
		z=fa[y];
		f=ch[y][0]==x;
		b=ch[x][f];
		fa[x]=z;
		fa[y]=x;
		if(b)fa[b]=y;
		ch[x][f]=y;
		ch[y][f^1]=b;
		if(ch[z][0]==y)ch[z][0]=x;
		if(ch[z][1]==y)ch[z][1]=x;
		pushup(y);
		pushup(x);
	}
	int insert(int&x,int d){
		if(x==0)return x=node(d);
		int k;
		if(d<=v[x]){
			k=insert(l(x),d);
			if(!fa[l(x)])fa[l(x)]=x;
		}else{
			k=insert(r(x),d);
			if(!fa[r(x)])fa[r(x)]=x;
		}
		pushup(x);
		return k;
	}
	void ins(int&x,int d){
		int k=insert(x,d);
		while(fa[k]&&fix[k]>fix[fa[k]])rot(k);
		while(fa[x])x=fa[x];
	}
	int query(int x,int d){
		if(x==0)return 0;
		if(d>=v[x])return s[l(x)]+1+query(r(x),d);
		return query(l(x),d);
	}
	void rec(int x){
		if(!x)return;
		st[++tp]=x;
		rec(l(x));
		rec(r(x));
	}
	int stk[100010],top;
	int build(int*p,int n){
		int x,las,i;
		sort(p+1,p+n+1);
		top=0;
		for(i=1;i<=n;i++){
			x=node(p[i]);
			las=0;
			while(top&&fix[stk[top]]<fix[x]){
				pushup(stk[top]);
				las=stk[top--];
			}
			if(top)r(stk[top])=x;
			l(x)=las;
			stk[++top]=x;
		}
		while(top)pushup(stk[top--]);
		return stk[1];
	}
}
void drec(int fa,int x){
	using namespace tree;
	using namespace treap;
	rec(r1[x]);
	r1[x]=0;
	rec(r2[x]);
	r2[x]=0;
	for(int i=h[x];i;i=nex[i]){
		if(to[i]!=fa&&d[to[i]]>=lim)drec(x,to[i]);
	}
}
int p[100010],N,u;
void dfs3(int fa,int x){
	using namespace tree;
	p[++N]=getdis(x,u)-r[x];
	for(int i=h[x];i;i=nex[i]){
		if(ok)dfs3(x,to[i]);
	}
}
int solve(int fa,int x){
	using namespace tree;
	dfs1(0,x);
	mn=inf;
	n=siz[x];
	dfs2(0,x);
	if(fa){
		u=fa;
		N=0;
		dfs3(0,x);
		r2[cn]=treap::build(p,N);
	}
	x=cn;
	vis[x]=C;
	df[x]=fa;
	d[x]=d[fa]+1;
	u=x;
	N=0;
	dfs3(0,x);
	r1[x]=treap::build(p,N);
	sz[x]=1;
	for(int i=h[x];i;i=nex[i]){
		if(ok)sz[x]+=sz[solve(x,to[i])];
	}
	return x;
}
void rebuild(int fa,int x){
	using namespace tree;
	lim=d[x];
	drec(0,x);
	C++;
	solve(fa,x);
}
namespace dtree{
	const double al=.85;
	int fa[100010],rt1[100010],rt2[100010],siz[100010],dep[100010];
	//rt1:root=self,rt2:root=fa->self_area_1st
	void addnode(int x){
		fa[x]=tree::fa[x][0];
		dep[x]=dep[fa[x]]+1;
		int u,t;
		for(u=x;fa[u];u=fa[u]){
			t=getdis(x,fa[u]);
			ans+=treap::query(rt1[fa[u]],r[x]-t);
			ans-=treap::query(rt2[u],r[x]-t);
		}
		treap::ins(rt1[x],-r[x]);
		for(u=x;fa[u];u=fa[u]){
			t=getdis(x,fa[u]);
			treap::ins(rt1[fa[u]],t-r[x]);
			treap::ins(rt2[u],t-r[x]);
		}
		for(u=x;u;u=fa[u])siz[u]++;
		t=0;
		for(u=x;fa[u];u=fa[u]){
			if(siz[u]>al*siz[fa[u]])t=fa[u];
		}
		if(t)rebuild(fa[t],t);
	}
}
int main(){
	using namespace treap;
	using namespace dtree;
	srand(19260817);
	int n,i,a,c;
	d=dep;
	r1=rt1;
	r2=rt2;
	sz=siz;
	df=dtree::fa;
	scanf("%d%d",&a,&n);
	scanf("%d%d%d",&a,&c,r+1);
	ins(rt1[1],-r[1]);
	d[1]=1;
	sz[1]=1;
	puts("0");
	for(i=2;i<=n;i++){
		scanf("%d%d%d",&a,&c,r+i);
		a^=(ans%1000000000);
		tree::add(a,i,c);
		addnode(i);
		printf("%lld\n",ans);
	}
}

[UOJ55]紫荆花之恋的更多相关文章

  1. bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 && AC400

    3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec  Memory Limit: 512 MBSubmit: 159  Solved: 40[Submit][Status] ...

  2. 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT

    [BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...

  3. BZOJ 3435: [Wc2014]紫荆花之恋

    二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 ...

  4. luogu P3920 [WC2014]紫荆花之恋

    LINK:紫荆花之恋 每次动态加入一个节点 统计 有多少个节点和当前节点的距离小于他们的权值和. 显然我们不能n^2暴力. 考虑一个简化版的问题 树已经给出 每次求某个节点和其他节点的贡献. 不难想到 ...

  5. 【WC2014】紫荆花之恋(替罪羊重构点分树 & 平衡树)

    Description 若带点权.边权的树上一对 \((u, v)\) 为 friend,那么需要满足 \(\text{dist}(u, v) \le r_u + r_v\),其中 \(r_x\) 为 ...

  6. BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋

    https://www.lydsy.com/JudgeOnline/problem.php?id=3435 https://www.luogu.org/problemnew/show/P3920 ht ...

  7. 数据结构(平衡树,树分治,暴力重构):WC 2014 紫荆花之恋

    [题目描述] 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这棵大树实际上是一个带权 ...

  8. [WC 2014]紫荆花之恋

    Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上 ...

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

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ55.html 题解 做法还是挺容易想到的. 但是写的话…… 首先这种题如果只要求一棵树中的满足条件的点数( ...

随机推荐

  1. Computer(HDU2196+树形dp+树的直径)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2196 题目: 题意:有n台电脑,每台电脑连接其他电脑,第i行(包括第一行的n)连接u,长度为w,问你每 ...

  2. 多种方法过Codeforces Round #270的A题(奇偶法、打表法和Miller_Rabin(这个方法才是重点))

    题目链接:http://codeforces.com/contest/472/problem/A 题目: 题意:哥德巴赫猜想是:一个大于2的素数一定可以表示为两个素数的和.此题则是将其修改为:一个大于 ...

  3. 阿里云服务器下安装配置 vsftpd —— 基于CentOS 6.3 【简洁版】

    原文链接:http://www.tuicool.com/articles/nuiQBja 1.更新yum源 我是直接 yum update 更新的 2.安装vsftp 使用yum命令安装vsftpd ...

  4. fork与printf缓冲问题

    printf输出条件: (1) 调用fflush: (2) 缓冲区满了: (3) 遇到\n \r这些字符 (4) 遇到scanf这些要取缓冲区的: (5) 线程或者进程退出: fork之后会拷贝父进程 ...

  5. 【设计模式】享元模式(Flyweight)

    摘要: 1.本文将详细介绍享元模式的原理和实际代码中特别是Android系统代码中的应用. 纲要: 1. 引入享元模式 2. 享元模式的概念及优缺点介绍 3. 享元模式在Android源码中的应用 1 ...

  6. 移动端测试===Android内存管理: 理解App的PSS

    Android内存管理: 理解App的PSS 原文链接:http://www.littleeye.co/blog/2013/06/11/android-memory-management-unders ...

  7. free函数在操作系统内存中的实现【转】

    转自:http://www.2cto.com/kf/201210/160985.html 我一次性malloc十个单位节点的内存空间出来赋值给L, 现在我想一次性删除从第3个到第6个节点,我是这么做的 ...

  8. webview loadRequest

    // 所构建的NSURLRequest具有一个依赖于缓存响应的特定策略,cachePolicy取得策略,timeoutInterval取得超时值 [self.yourSite loadRequest: ...

  9. 搭建selenium+python自动化环境

    1.安装python,下载地址:http://python.org---安装版本3.5.1 ps:自带setuptools和pip工具 2.然后,用pip安装开发Web App需要的第三方库:异步框架 ...

  10. UNIX shell 学习笔记 一 : 几个shell的规则语法对比

    1. 查看系统有哪些可用的shell cat /etc/shell 2. 每种shell都有一个特殊内置变量来存上一条命令的退出状态,例: C/TC shell $status % cp fx fy ...