我们尝试枚举,若把3个点不同排列也算入,发现每个边都被计算了
左子树点数 * 右子树点数
于是树形dp跑一边,每次更改一条边从新计算和就行了(别忘了除C(n,3))
下面是ac代码:
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
const int N = 6e5+5;
int w[N];
int he[N], ver[N], ne[N];
ll f[N], e[N];
int tot;
int gg[N];
int n;
ll ans;
void init()
{
tot = 0;
memset(he, 0, sizeof(he));
memset(ne, 0, sizeof(ne));
memset(f, 0, sizeof(f));
}
void add(int x, int y, int w)
{
ver[++tot] = y;
e[tot] = w;
ne[tot] = he[x];
he[x] = tot;
}
void dp(int x, int fa)
{
for (int i = he[x]; i; i = ne[i])
{
int y = ver[i];
if (y == fa) continue;
dp(y, x);
ans += f[y] * e[i] * (n - f[y]);
gg[i] = f[y];
f[x] += f[y];
}
f[x]++;
}
ll C(ll n)
{
return (n) * (n-1) * (n - 2) / 6;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie();
init();
cin >> n;
for (int i = 1; i < n; i++)
{
int x, y, w;
cin >> x >> y >> w;
add(x, y, w);
add(y, x, w);
}
int q;
dp(1, 0);
ll ggs = C(n);
cin >> q;
while(q--)
{
ll x, w;
cin >> x >> w;
x *= 2;
ll fk = gg[x];
if (fk == 0) fk = gg[x-1];
ans = ans - e[x] * fk * (n - fk) + w * fk * (n - fk);
e[x] = e[x-1] = w;
printf("%.10f\n", (double)ans * (n - 2) / ggs);
}
return 0;
}