虫子

题目

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;
}
原文地址:https://www.cnblogs.com/2016gdgzoi509/p/11324351.html