CF482E ELCA

XXIV.CF482E ELCA

Difficulty 3200的大神题。

这题维护应该很好想:与其维护所有对的LCA,不如维护一个数是多少对的LCA。显然,这个数量应该为szx2ysonxszy2。其中szxx子树的大小,sonxx的儿子集合。

然后套LCT。我们需要维护这样一些东西:

szi:虚子树的sz

szr:实子树的sz

sumi:虚子树的答案和

sumr:实子树的答案和

sqi:虚子树的平方和

val:节点的值

all

定义比较复杂。以节点i为根的实子树,是以(以节点i为根的splay中所有节点的虚子树)的并集。则allx的定义是这些虚子树的szi乘上虚子树的根的val的和。

为什么我们要有这么个鬼东西?

因为,左儿子实际上是x的祖先。这些东西要在求左儿子的贡献时用上。

维护:

szrx=szix+szrlson+szrrson

allx=alllson+allrson+szixvalx

重头戏来了:

sumx=

sumlson+sumrson(子节点的sum传递上来)

+valx(szix2sqix) (虚子树的贡献)

+2valxszixszrrson (右儿子的贡献,虚子树中任何一个点与右儿子实子树中任何一个点的LCA都是x

+2alllson(szrxszrlson) (虚子树和右子树里面任何一个点,在原树中都是左子树中点的子孙。故allx对于左子树中每一个点都已经储存下来了贡献,再乘上虚子树+右子树的贡献,即是正确贡献)

然后这题并不需要换根。因此实际上treap什么的也可以胜任这道题?

这题之所以difficulty 3200,我估计八成是因为这个pushup太毒瘤了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,head[50100],cnt,FA[50100],res;
#define lson t[x].ch[0]
#define rson t[x].ch[1]
struct LCT{
	int fa,ch[2],val,szi,szr,sqi,sumi,sumr,all;
}t[50100];
inline int identify(int x){
	if(x==t[t[x].fa].ch[0])return 0;
	if(x==t[t[x].fa].ch[1])return 1;
	return -1;
}
inline void pushup(int x){
	t[x].szr=t[x].szi+t[lson].szr+t[rson].szr,t[x].sumr=t[x].sumi+t[lson].sumr+t[rson].sumr;
	t[x].all=t[lson].all+t[rson].all+t[x].szi*t[x].val;
	t[x].sumr+=t[x].val*(t[x].szi*t[x].szi-t[x].sqi);
	t[x].sumr+=2*t[x].val*t[x].szi*t[rson].szr;
	t[x].sumr+=2*t[lson].all*(t[x].szr-t[lson].szr); 
}
inline void rotate(int x){
	register int y=t[x].fa;
	register int z=t[y].fa;
	register int dirx=identify(x);
	register int diry=identify(y);
	register int b=t[x].ch[!dirx];
	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
	if(b)t[b].fa=y;t[y].ch[dirx]=b;
	t[y].fa=x,t[x].ch[!dirx]=y;
	pushup(y),pushup(x);
}
inline void splay(int x){
	while(identify(x)!=-1){
		register int fa=t[x].fa;
		if(identify(fa)==-1)rotate(x);
		else if(identify(x)==identify(fa))rotate(fa),rotate(x);
		else rotate(x),rotate(x);
	}
}
inline void access(int x){
	for(register int y=0;x;x=t[y=x].fa){
		splay(x);
		t[x].sqi+=t[rson].szr*t[rson].szr;
		t[x].sumi+=t[rson].sumr;
		t[x].szi+=t[rson].szr;
		rson=y;
		t[x].sqi-=t[rson].szr*t[rson].szr;
		t[x].sumi-=t[rson].sumr;
		t[x].szi-=t[rson].szr;
		pushup(x);
	}
}
struct Edge{
	int to,next;
}edge[50100];
void ae(int u,int v){
	edge[cnt].next=head[u],edge[cnt].to=v,head[u]=cnt++;
}
void dfs(int x){
	t[x].szi=1;
	for(int i=head[x];i!=-1;i=edge[i].next)dfs(edge[i].to),t[x].szi+=t[edge[i].to].szr,t[x].sumi+=t[edge[i].to].sumr,t[x].sqi+=t[edge[i].to].szr*t[edge[i].to].szr;
	pushup(x);
}
void cut(int x){
	access(FA[x]),splay(FA[x]),splay(x);
	t[x].fa=0;
	t[FA[x]].szi-=t[x].szr;
	t[FA[x]].sumi-=t[x].sumr;
	t[FA[x]].sqi-=t[x].szr*t[x].szr;
	pushup(FA[x]);
}
void link(int x,int y){
	access(y),splay(y),access(x),splay(x);
	t[y].fa=x;
	t[t[y].fa].szi+=t[y].szr;
	t[t[y].fa].sumi+=t[y].sumr;
	t[t[y].fa].sqi+=t[y].szr*t[y].szr;
	pushup(t[y].fa);	
}
bool che(int x,int y){
	access(y),splay(y),splay(x);
	return identify(y)!=-1;
}
void qwq(int x,int y){
	if(FA[x]==y||FA[y]==x)return;
	if(che(x,y))swap(x,y);
	cut(x);
	FA[x]=y;
	link(y,x);
}
void qaq(int x,int y){
	access(x),splay(x),t[x].val=y,pushup(x);
}
inline void read(int &x){
	x=0;
	char c=getchar();
	while(c>'9'||c<'0')c=getchar();
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
signed main(){
	read(n),memset(head,-1,sizeof(head));
	for(int i=2;i<=n;i++)read(FA[i]),ae(FA[i],i),t[i].fa=FA[i];
	for(int i=1;i<=n;i++)read(t[i].val),t[i].szi=1;
	dfs(1),res=t[1].sumr,printf("%.9lf\n",1.0*res/(n*n));
//	for(int j=1;j<=n;j++)printf("(F:%d,L:%d,R:%d,SQ:%d,AI:%d,AR:%d,SI:%d,SR:%d,V:%d)\n",t[j].fa,t[j].ch[0],t[j].ch[1],t[j].sqi,t[j].sumi,t[j].sumr,t[j].szi,t[j].szr,t[j].val);
	scanf("%d",&m);
	for(int i=1,x,y;i<=m;i++){
		char s[3];
		scanf("%s",s),read(x),read(y);
		if(s[0]=='P')qwq(x,y);
		else qaq(x,y);
		access(1),splay(1),res=t[1].sumr;
		printf("%.9lf\n",1.0*res/(n*n));
	}
	return 0;
}

posted @   Troverld  阅读(57)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示