• 虫子


    题目

    Description

    Input

    Output

    题解

    先考虑暴力做法。 可以直接树形dp。
    (val_x = sum_{i=1}^{n}sum_{j=1}^{n}[lca(i,j)=x])这可以很容易dp出来。
    答案显然是(frac{sum_{i}val_x imes w_x}{n^2})

    我们可以用LCT维护

    考虑操作1, 我们可以先把(x)(f_x)的连边删除, 计算对答案的影响, 再把(x)(y)连上, 再算一遍对答案的影响。
    先考虑删边, 设(y)(x)点的祖先, (z)(y)的儿子同时也在(x)(y)的树链上。那么(val_x -= 2siz_x(siz_y-siz_z))
    于是我们需要在lct上维护(val_x)(siz_x)(num_x = siz_{f_x} - siz_x), 那么一次删边对单个祖先节点的影响就是(2 imes siz_x imes num_x)
    于是我们还要维护贡献的和(sum_x)
    连边同理。

    操作2直接改就行了。

    注意: 有根树要避免makeroot, 因为makeroot的实质是将一条树链的父子关系反向。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    
    typedef long long LL;
    
    
    const int N = 100010, M = 200010;
    
    
    LL w[N];
    
    
    LL Answer;
    
    
    namespace LCT
    {
    	const int SIZE = 1e5 + 10;
    	
    	int ch[SIZE][2], fa[SIZE];
    	LL siz[SIZE], tag[SIZE], num[SIZE], val[SIZE], sum[SIZE];
    
    	inline int isroot(int x) { return !fa[x] || (ch[fa[x]][0] != x && ch[fa[x]][1] != x); }
    
    	inline int c(int x) { return ch[fa[x]][1] == x; }
    
    	inline void downtag(int x, LL v)
    	{
    		tag[x] += v;
    		siz[x] += v;
    		val[x] += 2LL * v * num[x];
    	}
    
    	inline void pushdown(int x)
    	{
    		if (!tag[x]) return;
    		if (ch[x][0]) downtag(ch[x][0], tag[x]);
    		if (ch[x][1]) downtag(ch[x][1], tag[x]);
    		tag[x] = 0;
    	}
    
    	inline void maintain(int x)
    	{
    		if (!x) return;
    		sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + w[x] * num[x];
    	}
    
    	void rotate(int x)
    	{
    		int f = fa[x], p = fa[f], d = c(x);
    		if (!isroot(f)) ch[p][c(f)] = x;
    		fa[x] = p;
    		ch[f][d] = ch[x][d^1]; fa[ch[f][d]] = f;
    		ch[x][d^1] = f; fa[f] = x;
    		maintain(f); maintain(x);
    	}
    
    	void push(int x)
    	{
    		if (!isroot(x)) push(fa[x]);
    		pushdown(x);
    	}
    
    	void splay(int x)
    	{
    		push(x);
    		while (!isroot(x))
    		{
    			int f = fa[x];
    			if (!isroot(f))
    			{
    				if (c(x) == c(f)) rotate(f);
    				else rotate(x);
    			}
    			rotate(x);
    		}
    		maintain(x);
    	}
    
    	void access(int x)
    	{
    		for (int v = 0; x; v = x, x = fa[x])
    		{
    			if (v)
    			{
    				while (ch[v][0]) v = ch[v][0];
    				splay(v);
    			}
    			splay(x);
    			ch[x][1] = v;
    			num[x] = siz[x] - siz[v];
    			maintain(x);
    		}
    	}
    	
    	void cut(int x)
    	{
    		access(x);
    		splay(x);
    		int f = ch[x][0];
    		Answer -= 2LL * sum[f] * siz[x];
    		downtag(f, -siz[x]);
    		fa[f] = 0;
    		ch[x][0] = 0;
    		maintain(x); maintain(f);
    	}
    
    	void link(int x, int y)
    	{
    		access(y);
    		splay(y);
    		Answer += 2LL * sum[y] * siz[x];
    		downtag(y, siz[x]);
    		maintain(y);
    		fa[x] = y;
    		access(x);
    		splay(x);
    	}
    
    	bool check(int x, int y)
    	{
    		access(y);
    		splay(y);
    		splay(x);
    		return !fa[x];
    	}
    }
    
    
    struct edge
    {
    	int from, to;
    	edge() { }
    	edge(int _1, int _2) : from(_1), to(_2) { }
    } edges[M];
    
    int head[N], nxt[M], tot;
    
    inline void init()
    {
    	memset(head, -1, sizeof(head));
    	tot = 0;
    }
    
    inline void add_edge(int x, int y)
    {
    	edges[tot] = edge(x, y);
    	nxt[tot] = head[x];
    	head[x] = tot++;
    	edges[tot] = edge(y, x);
    	nxt[tot] = head[y];
    	head[y] = tot++;
    }
    
    void dfs(int x, int fa)
    {
    	LCT::siz[x] = 1;
    	LCT::val[x] = 1;
    	
    	for (int i = head[x]; ~i; i = nxt[i])
    	{
    		edge & e = edges[i];
    		if (e.to != fa)
    		{
    			dfs(e.to, x);
    			LCT::val[x] += 2LL * LCT::siz[x] * LCT::siz[e.to];
    			LCT::siz[x] += LCT::siz[e.to];
    		}
    	}
    	LCT::num[x] = LCT::siz[x];
    	Answer += LCT::val[x] * w[x];
    	
    	LCT::maintain(x);
    }
    
    
    int father[N];
    
    
    int n, m;
    
    
    int main()
    {
    	scanf("%d", &n);
    	
    	init();
    	
    	for (int i = 2; i <= n; i++)
    	{
    		scanf("%d", &father[i]);
    		LCT::fa[i] = father[i];
    		add_edge(i, father[i]);
    	}
    	
    	for (int i = 1; i <= n; i++)
    		scanf("%lld", &w[i]);
    	
    	dfs(1, 0);
    	
    	printf("%lf
    ", (double) Answer / (double) (1LL * n * n));
    	
    	scanf("%d", &m);
    	
    	for (int i = 1; i <= m; i++)
    	{
    		int opt;
    		scanf("%d", &opt);
    		
    		if (opt == 1)
    		{
    			int x, y;
    			scanf("%d %d", &x, &y);
    			if (LCT::check(x, y)) swap(x, y);
    			LCT::cut(x);
    			father[x] = y;
    			LCT::link(x, y);
    		}
    		else
    		{
    			int x, v;
    			scanf("%d %d", &x, &v);
    			LCT::access(x);
    			LCT::splay(x);
    			Answer += LCT::val[x] * (v - w[x]);
    			w[x] = v;
    		}
    		
    		printf("%lf
    ", (double) Answer / (double) (1LL * n * n));
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    eval(data)和eval("("+data+")")的区别
    来自 119.*.*.*的回复: TTL 传输中过期
    小伙伴们,我们一起奋斗吧
    计算机专业学习课程推荐
    sysbench使用教程【转载】
    Docker学习笔记 — Docker私有仓库搭建【转载】
    Linux设置静态IP【转】
    聊一聊PV和并发、以及计算web服务器的数量的方法【转】
    Linux常用Shell脚本珍藏【转载】
    戏说云计算之PaaS,IaaS,SaaS【转载】
  • 原文地址:https://www.cnblogs.com/2016gdgzoi509/p/11324351.html
Copyright © 2020-2023  润新知