虫子
题目
Description
Input
Output
题解
先考虑暴力做法。 可以直接树形dp。
令\(val_x = \sum_{i=1}^{n}\sum_{j=1}^{n}[lca(i,j)=x]\)这可以很容易dp出来。
答案显然是\(\frac{\sum_{i}val_x \times 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 \times siz_x \times 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\n", (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\n", (double) Answer / (double) (1LL * n * n));
}
return 0;
}