[WC2014] 紫荆花之恋

链接_

终于过了。。。

首先这题显然只能一个个处理,即每加入一个点计算贡献该点与其他点的贡献。

考虑转换题意:假设我们找到一个 \(p\)\(u\rightarrow v\) 路径上,那么 \(dis(u,v)=dis(u,p)+dis(p,v)\)

那么就有 \(dis(u,p)+dis(v,p)\leq d_u+d_v\),移项得 \(d_u-dis(u,p)\geq dis(v,p)-d_v\)

我们假设 \(u\) 是我们当前的加入点,那么我们令 \(w_{p,v}=dis(v,p)-d_v\),最后我们只需要对于所有 \(p\) 求其中 \(\leq d_u-dis(u,p)\) 的个数即可。这个就是一个平衡树维护的东西。

直接计算显然是 \(O(n^2\log n)\)。但按照套路,考虑点分树,可以把需要计算的 \(p\) 缩小到 \(O(\log n)\) 个。而且根据点分治的性质,\(u\)\(v\) 的路径上有且仅有一个点在点分树上同时为 \(u,v\) 的祖先,所以可以计算。

但是我们还有一个前提是 \(p\)\(u\rightarrow v\) 路径上。如果直接按上述方式计算,上层分治中心也会计算这个值。按照点分治的套路,考虑容斥,即对于当前待插入的点 \(u\),我们在它的每个点分祖先 \(p\) 上都再维护一个平衡树表示容斥的标记,然后当处理到 \(p\) 时,我们减去这个标记,表示 \(u\) 已经处理过了,抵消上层分治中心的处理 \(u\) 的贡献。

这样就处理完了点分树的部分,时间复杂度 \(O(n\log^2n)\)。但是当你兴奋地打完代码后,才发现这题最毒瘤的地方:强制在线。(毒瘤题居然不给离线一分)。

对于这类强制在线题目有一个比较暴力的通解,就是根号重构。但是这里会退化成 \(O(n\sqrt n\log^2 n)\) 显然爆炸。

但是我们考虑,虽然动态加点可能会导致整颗点分树全部发生变化,但是我们并不需要它一定是点分树,我们只需要它的树深是 \(O(\log n)\) 的就好了。这个在平衡树中有一个很经典的数据结构:替罪羊树。

具体来说,我们定义一个常数 \(\alpha\),只有其中一颗子树的大小超过整棵树的 \(n\times\alpha\),我们才对这棵树进行重建。

所以插入一个点时,我们对它在点分树上的祖先找到第一个不平衡的树,然后对这棵子树重新构造点分树。

在替罪羊树中 \(\alpha=0.7\) 左右比较优秀,但是这里要稍微大一些,因为一次重构的代价是 \(O(s_u\log^2 s_u)\) 的。

具体复杂度我也不会证,但好像均摊下来不超过 \(O(n\log^3 n)\) ,稍微卡卡常就能过。

还有这里如果用splay/非旋treap会被卡常。。。

当然写有旋treap就没这么多事了。

然后由于这题有很多次重构,总重构复杂度会到 \(O(n\log^3 n)\),但是实际用的点只有 \(O(n\log^2 n)\),所以要垃圾回收。

最后由于要重构很多次,rand()好像有点慢。。。不如直接模仿替罪羊树的写法,不平衡就旋(怎么感觉在哪里看到过这种操作),也不知道为什么这样就会变快?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define N 100010
#define M N*40
using namespace std;
const double alp=0.85;
int nxt[N<<1],to[N<<1],w[N<<1],head[N],cnt;
void add(int u,int v,int w1)
{
	nxt[++cnt]=head[u];
	to[cnt]=v;
	w[cnt]=w1;
	head[u]=cnt;
}
int val[M],rnd[M],ch[M][2],siz[M],tot;	
int sta[M],totp;
void update(int u){siz[u]=siz[ch[u][0]]+siz[ch[u][1]]+1;}
int new_node(int v)
{
	int u=totp?sta[totp--]:++tot;
//	if(tot>M-10) throw;
//	rnd[u]=rand();
	val[u]=v;siz[u]=1;ch[u][0]=ch[u][1]=0;
	return u;
}
void rotate(int &u,int d)
{
	int v=ch[u][d];
//	if(rnd[u]<rnd[v]) return;
	if(siz[u]*0.75>siz[v]) return;
	ch[u][d]=ch[v][!d],ch[v][!d]=u;
	update(u);update(v);u=v;
}
void insert(int &u,int v)
{
	if(!u){u=new_node(v);return;}
	siz[u]++;
	if(v<=val[u]) insert(ch[u][0],v),rotate(u,0);
	else insert(ch[u][1],v),rotate(u,1);
}
void clear(int &u){if(!u) return;clear(ch[u][0]),clear(ch[u][1]);sta[++totp]=u;u=siz[u]=rnd[u]=0;}
int find(int u,int v)
{
	if(!u) return 0;
	if(val[u]<=v) return find(ch[u][1],v)+siz[ch[u][0]]+1;
	return find(ch[u][0],v);
}
int r1[N],r2[N],asiz[N];
int vis[N],ut;
int usiz,root,msiz;
void dfs1(int u,int f)
{
	clear(r1[u]),clear(r2[u]);asiz[u]=1;
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(vis[v]==ut || v==f) continue;
		dfs1(v,u);
		asiz[u]+=asiz[v];
	}
}
void dfs2(int u,int f)
{
	int res=usiz-asiz[u];
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(vis[v]==ut || v==f) continue;
		dfs2(v,u);
		res=max(res,asiz[v]);
	}
	if(res<msiz) msiz=res,root=u;
}
int qu[N],qd[N],qtot;
void dfs3(int u,int f,int d)
{
	qu[++qtot]=u,qd[qtot]=d;
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(vis[v]==ut || v==f) continue;
		dfs3(v,u,d+w[i]);
	}
}
int dep[N],c[N];
#define D 110
int f[N][D],fd[N][D];

void rebuild(int u,int d)
{
	dfs1(u,0);
	usiz=msiz=asiz[u];root=u;
	dfs2(u,0);
	qtot=0;
	dep[u=root]=d;
	vis[u]=ut;
	dfs3(u,0,0);
	for(int i=1;i<=qtot;i++)
	{
		int v=qu[i];
		f[v][d]=u,fd[v][d]=qd[i];
		insert(r1[u],fd[v][d]-c[v]);
		if(d) insert(r2[u],fd[v][d-1]-c[v]);
	}
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(vis[v]!=ut) rebuild(v,d+1);
	}
}
int main()
{
	int n;
	scanf("%*d%d%*d%*d%d",&n,&c[1]);
	f[1][0]=1,insert(r1[1],-c[1]);
	puts("0");
	long long ans=0;
	for(int i=2;i<=n;i++)
	{
		int fa,w;
		scanf("%d%d%d",&fa,&w,&c[i]);fa^=ans%1000000000;
		add(fa,i,w),add(i,fa,w);
		f[i][dep[i]=dep[fa]+1]=i,fd[i][dep[i]]=0;
		if(dep[i]>D-10) throw;
		for(int j=0;j<dep[i];j++)
		{
			f[i][j]=f[fa][j];
			fd[i][j]=fd[fa][j]+w;
			ans+=find(r1[f[i][j]],c[i]-fd[i][j]);
			if(j) ans-=find(r2[f[i][j]],c[i]-fd[i][j-1]);
		}
		for(int j=0;j<=dep[i];j++)
		{
			insert(r1[f[i][j]],fd[i][j]-c[i]);
			if(j) insert(r2[f[i][j]],fd[i][j-1]-c[i]);
		}
		++ut;
		for(int j=0;j<dep[i];vis[f[i][j]]=ut,j++)
		if(siz[r1[f[i][j]]]*alp<siz[r1[f[i][j+1]]]){rebuild(f[i][j],j);break;}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2020-09-10 14:52  Flying2018  阅读(313)  评论(4编辑  收藏  举报