zoukankan      html  css  js  c++  java
  • CF482E ELCA

    一、题目

    点此看题

    二、解法

    题目的提示已经足够明显了吧,肯定是要写一个 ( t link-cut-tree) 。我们只需要求出总和,再除以方案数就是期望。然后可以算每个点为 ( t lca) 时的贡献。

    但是要对子树搞点事情,而子树分为虚子树和实子树(看连接的是什么边),所以两类都要维护,实子树可以用 ( t push\_up) 操作维护,虚子树要在变动父子关系的时候维护,有点麻烦。要维护下列信息:

    • (x) 的虚子树大小(包括 (x) 这个点):(siz)
    • (x) 的子树中节点总个数:(sum)
    • (x) 节点包括其子树中的每个点的答案和:(ans)
    • (x) 子树内所有点的 (siz imes a)(all)
    • (x) 所有虚子树的答案:(ad)
    • (x) 所有虚子树 (siz) 的平方求和:(de)

    虚子树的信息你肯定是会维护的,(sum)(ans) 你肯定也会,我就来详细讲一讲 (ans) 怎么算,要分成四部分:

    • 拿到其子树的答案:(ans[ls]+ans[rs]+ad[x])
    • 虚子树之间的贡献,也就是在 (siz[x]) 中乱选两个点,再把两点选在同一个子树中的方案给删掉:((siz[x] imes siz[x]-de[x]) imes a[x])
    • 虚子树和实子树((x) 的右儿子)之间的贡献,它们的 ( t lca)(x)(2 imes a[x] imes siz[x] imes sum[ch[x][1]])
    • (x) 的子树和 (x) 祖先的贡献((x) 的左儿子),这个贡献由于 ( t splay) 结构的原因没有被统计到,反正是求总和,我们把这个贡献放在 (x) 这里也没关系:(2 imes all[ch[x][0]] imes (sum[x]-sum[ch[x][0]]))

    然后因为要保证是有根树所以不能用 ( t makeroot) 。那么 ( t link(x,y)) 就把 (x,y) 都转到根然后连虚边,( t cut) 就把 (x) 转到根,(y) 转到 (x) 下面然后删虚边。在 ( t access,cut,link) 的时候都要改虚子树的信息哦。

    反正写的时候就是非常爽,非常爽。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 50005;
    #define int long long 
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[M],siz[M],sum[M],ans[M];char s[10];
    int fa[M],all[M],ad[M],de[M],ch[M][2],par[M];double r;
    /*
    siz[x]表示x的虚子树大小(包括x)
    sum[x]表示x为根的子树节点个数
    ans[x]表示x的答案
    all[x]表示x子树内的siz[y]*a[y]
    ad[x]表示x虚子树的答案 
    de[x]表示x的虚子树的sum的平方的和 
    */
    void up(int x)
    {
    	sum[x]=siz[x]+sum[ch[x][0]]+sum[ch[x][1]];
    	all[x]=all[ch[x][0]]+all[ch[x][1]]+a[x]*siz[x];
    	ans[x]=ans[ch[x][0]]+ans[ch[x][1]]+ad[x]//第一部分,直接累加 
    	+a[x]*(siz[x]*siz[x]-de[x])//第二部分,虚子树的贡献
    	+2*a[x]*sum[ch[x][1]]*siz[x]//第三部分,虚实之间的贡献,lca是x
    	+2*all[ch[x][0]]*(sum[x]-sum[ch[x][0]]);//第四部分,把祖先的答案算到它上面 
    }
    int nrt(int x)//判断是不是实边 
    {
    	return ch[par[x]][0]==x || ch[par[x]][1]==x;
    }
    int chk(int x)//判断是哪个儿子
    {
    	return ch[par[x]][1]==x;
    }
    void rotate(int x)
    {
    	int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];
    	ch[y][k]=w;par[w]=y;
    	if(nrt(y)) ch[z][chk(y)]=x;par[x]=z;
    	ch[x][k^1]=y;par[y]=x;
    	up(y);up(x);
    }
    void splay(int x)//转到实链的最上面
    {
    	while(nrt(x))
    	{
    		int y=par[x];
    		if(nrt(y))
    		{
    			if(chk(x)==chk(y)) rotate(y);
    			else rotate(x);
    		}
    		rotate(x);
    	}
    }
    void access(int x)
    {
    	for(int y=0;x;x=par[y=x])
    	{
    		splay(x);
    		//先把ch[x][1]加进虚子树中
    		siz[x]+=sum[ch[x][1]];
    		ad[x]+=ans[ch[x][1]];
    		de[x]+=sum[ch[x][1]]*sum[ch[x][1]];
    		//再把y从虚子树中拿出来
    		siz[x]-=sum[y];
    		ad[x]-=ans[y];
    		de[x]-=sum[y]*sum[y]; 
    		ch[x][1]=y;up(x);
    	}
    }
    void link(int x,int y)//把(x,y)连一条边,x是祖先 
    {
    	access(y);
    	splay(y);
    	access(x);
    	splay(x);
    	par[y]=x;
    	siz[x]+=sum[y];
    	ad[x]+=ans[y];
    	de[x]+=sum[y]*sum[y];
    	up(x);
    }
    void cut(int x,int y)//把(x,y)这条边去掉,x是祖先
    {
    	access(x);
    	splay(x);
    	splay(y);
    	par[y]=0;
    	siz[x]-=sum[y];
    	ad[x]-=ans[y];
    	de[x]-=sum[y]*sum[y];
    	up(x);
    }
    int check(int x,int y)//判断x是不是y的祖先
    {
    	access(y);
    	splay(y);
    	splay(x);
    	return nrt(y);
    }
    signed main()
    {
    	n=read();
    	for(int i=2;i<=n;i++)
    		fa[i]=read();
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=ans[i]=all[i]=read();
    		siz[i]=sum[i]=1;
    	}
    	for(int i=2;i<=n;i++)
    		link(fa[i],i);
    	m=read();
    	access(1);
    	splay(1);
    	r=ans[1];
    	printf("%.10lf
    ",r/n/n);
    	while(m--)
    	{
    		scanf("%s",s);
    		int x=read(),y=read();double r=0;
    		if(s[0]=='P')
    		{
    			if(check(x,y)) swap(x,y);
    			cut(fa[x],x);
    			fa[x]=y;
    			link(fa[x],x);
    			access(1);
    			splay(1);
    			r=ans[1];
    		}
    		else
    		{
    			access(x);
    			splay(x);
    			a[x]=y;
    			up(x);
    			r=ans[x]; 
    		}
    		printf("%.10lf
    ",r/n/n);
    	}
    }
    
  • 相关阅读:
    .NET Core 使用NPOI读取Excel返回泛型List集合
    C# 判别系统版本以及Win10的识别办法
    WPF 程序员休息数字时钟
    分享一个淘宝/天猫/京东/阿里 图片抓取工具
    记一次数据库同步经历(sql server 2008)
    datagridview 如何显示记载中
    关于如何解决bootstrap table 列 切换 刷新 高度不一样
    js 中 函数的返回值问题
    winform 实现定位
    winform 里 如何实现文件上传
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14223677.html
Copyright © 2011-2022 走看看