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);
    	}
    }
    
  • 相关阅读:
    Mayan游戏 (codevs 1136)题解
    虫食算 (codevs 1064)题解
    靶形数独 (codevs 1174)题解
    黑白棋游戏 (codevs 2743)题解
    神经网络 (codevs 1088) 题解
    The Rotation Game (POJ 2286) 题解
    倒水问题 (codevs 1226) 题解
    银河英雄传说 (codevs 1540) 题解
    生日蛋糕 (codevs 1710) 题解
    第一章 1.11 高阶函数
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14223677.html
Copyright © 2011-2022 走看看