WC2014-紫荆花之恋

一棵树,每条边有边权,点有点权\(r\)\(n\)次加入一个点,给出它与父亲的连边长度和它的点权,问此时总共有多少对点满足\(r_i+r_j\ge dist(i,j)\)\(n\le 10^5\)

分析

树上点对统计可以使用点分治。每次只需要统计新增了多少对满足条件的点。设一对点\((u,v)\)的lca为\(k\),那么条件可以转化为\(r_u-dist(u,k)\ge dist(v,k)-r_v\),每次新加入点直接挂在父亲下面并直接往上跳,设当前跳到点\(c\),只要统计经过这个点有多少满足条件的即可。我们拿着\(r_u-dist(u,k)\)去查询有多少个\(dist(v,k)-r_v\)小于等于它就好了。用平衡树来维护这个东西,查询完之后就把它插入平衡树里。

但这样明显会重复计算从同一个子树来的点,所以在每个点还要再开一个平衡树,记录一下子树中到这个点的父亲的值,减一下即可。

上面我们说直接把这个点挂在下面,这事实上是很不对的,一个链的情况就可以把这个卡掉。于是我们可以利用重构的思想,如果一个子树的大小大于它父亲子树大小的\(\alpha\),那么就找到最上面一个这样的位置,重构整颗子树,把它变成一个点分治树的样子。替罪羊树的时间复杂度分析说明这样做的复杂度为\(O(f(n)log _{\frac{1}{\alpha}}n)\),其中\(f(n)\)为重构大小为\(n\)的子树的复杂度。

这一题中,点分治树的深度是\(O(logn)\)的,所以一次重构的可以直接每个点往上跳\(O(logn)\)个父亲,每次直接向平衡树中插入,总复杂度为\(O(nlog^3n)\)

代码

弃坑啦!!!!写过splay,treap,奇怪重量平衡树,最后改成替罪羊树来作平衡树,现在uoj上80分

不过学到了一种点分治找中心的神奇写法,超级好写,只要两行~

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
	int x=0,f=1;
	char c=getchar();
	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
	return x*f;
}
void write(giant x) {
	if (!x) puts("0"); else {
		static char s[25];
		int tot=0;
		while (x) s[++tot]=x%10+'0',x/=10;
		while (tot) putchar(s[tot--]);
		puts("");
	}
}
const int maxn=1e5+1;
const int maxm=5e6;
const int maxj=17;
const double alpha=0.77;
giant ans=0;
int fa[maxn],d[maxn],rec[maxn],size[maxn],my[maxn],onf[maxn],n;
bool able[maxn];
vector<int> g[maxn];
inline int conv(int x) {
	return x^((int)(ans%(giant)1e9));
}
struct graph {
	int h[maxn],tot,f[maxn][maxj],dep[maxn],dis[maxn];
	graph ():tot(0) {
		for (int i=0;i<maxj;++i) f[1][i]=1;
	}
	void add(int u,int v,int w) {
		g[u].push_back(v);
		g[v].push_back(u);
		f[v][0]=u;
		for (int j=1;j<maxj;++j) f[v][j]=f[f[v][j-1]][j-1];
		dep[v]=dep[u]+1,dis[v]=dis[u]+w;
	}
	int lca(int x,int y) {
		if (dep[x]<dep[y]) swap(x,y);
		for (int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
		if (x==y) return x;
		for (int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
		return f[x][0];
	}
	int dist(int x,int y) {
		return dis[x]+dis[y]-2*dis[lca(x,y)];
	}
} G;
int aux[maxn];
struct Treap {
	struct node {
		int ch[2],fa,val,size;
	} t[maxm];
	int pool[maxm],per;
	Treap ():per(0) {
		for (int i=1;i<maxm;++i) pool[i]=i;
	}
	inline void update(int x) {
		t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
	}
	inline void delnode(int &x) {
		if (!x) return;
		t[x].ch[0]=t[x].ch[1]=t[x].fa=t[x].val=t[x].size=0;
		pool[per--]=x;
		x=0;
	}
	inline int newnode(int x) {
		int nw=pool[++per];
		t[nw].size=1;
		t[nw].val=x;
		return nw;
	}
	bool rson(int x) {
		return t[t[x].fa].ch[1]==x;
	}
	void travel(int x,int &len) {
		if (!x) return;
		travel(t[x].ch[0],len);
		aux[++len]=t[x].val;
		travel(t[x].ch[1],len);
		delnode(x);
	}
	int reb(int l,int r) {
		if (l>r) return 0;
		int mid=l+r>>1;
		int nw=newnode(aux[mid]);
		t[nw].ch[0]=reb(l,mid-1);
		t[nw].ch[1]=reb(mid+1,r);
		update(nw);
		return nw;
	}
	void rebid(int x) {
		int len=0;
		travel(x,len);
		int fat=t[x].fa;
		bool d=rson(x);
		int l=1,r=len;
		t[fat].ch[d]=reb(l,r);
		update(fat);
	}
	inline void insert(int &rt,int x) {
		int nw=newnode(x);
		if (!rt) {
			rt=nw;
			return;
		}
		int now=rt;
		while (true) {
			++t[now].size;
			int &tmp=t[now].ch[x>t[now].val];
			if (tmp) now=tmp; else {
				tmp=nw;
				break;
			}
		}
		int goat=0;
		for (;t[now].fa;now=t[now].fa) if (t[now].size>0.6*t[t[now].fa].size) goat=now;
		if (goat) rebid(goat);
	}
	inline int le(int now,int x) {
		int ret=0;
		while (now) {
			if (x>t[now].val) ret+=t[t[now].ch[0]].size+1; else
			if (x==t[now].val) ++ret;
			now=t[now].ch[x>t[now].val];
		}
		return ret;
	}
	void clear(int &x) {
		if (x) clear(t[x].ch[0]),clear(t[x].ch[1]);
		delnode(x);
	}
} tree;
inline void addit(int u,int v,int w) {
	G.add(u,v,w);
	fa[v]=u,d[v]=d[u]+1,size[v]=1;
}
inline bool bad(int x) {
	return size[x]>alpha*size[fa[x]];
}
void label(int x,int f,int no) {
	able[x]=true;
	for (int v:g[x]) if (v!=f && d[v]>d[no]) label(v,x,no);
}
int Size(int x,int f) {
	size[x]=1;
	for (int v:g[x]) if (v!=f && able[v]) size[x]+=Size(v,x);
	return size[x];
}
int Root(int x,int f,int hf) {
	for (int v:g[x]) if (v!=f && able[v] && size[v]>hf) return Root(v,x,hf);
	return x;
}
void rebuild(int x,int fat,int up) {
	int sz=Size(x,0),rt=Root(x,0,sz>>1);
	able[rt]=false,d[rt]=d[fa[rt]=fat]+1,size[rt]=size[x];
	tree.clear(my[rt]),tree.clear(onf[rt]);
	for (int v:g[rt]) if (able[v]) rebuild(v,rt,up);
	for (int y=rt;y!=up;y=fa[y]) {
		tree.insert(my[y],G.dist(y,rt)-rec[rt]);
		if (!fa[y]) break;
		tree.insert(onf[y],G.dist(fa[y],rt)-rec[rt]);
	}
}
int cs=0;
inline void work(int x) {
	int goat=0;
	for (int y=x;y;y=fa[y]) {
		int d=G.dist(x,y);
		ans+=tree.le(my[y],rec[x]-d);
		tree.insert(my[y],d-rec[x]);
		if (!fa[y]) break;
		d=G.dist(x,fa[y]);
		ans-=tree.le(onf[y],rec[x]-d);
		tree.insert(onf[y],d-rec[x]);
		++size[fa[y]];
		if (bad(y)) goat=y;
	}
	if (goat) ++cs,label(fa[goat],0,fa[fa[goat]]),rebuild(fa[goat],fa[fa[goat]],fa[fa[goat]]);
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
	freopen("my.out","w",stdout);
#endif
	srand(200);
	puts("0");
	read(),n=read(),read(),read(),rec[1]=read();
	tree.insert(my[1],-rec[1]),size[1]=1;
	for (int i=2;i<=n;++i) {
//		int x=conv(read());
		int x=read();
		int c=read();
		rec[i]=read();
		addit(x,i,c);
		work(i);
		write(ans);
	}
	return 0;
}
posted @ 2017-05-04 22:19  permui  阅读(372)  评论(0编辑  收藏  举报