BZOJ3451:Tyvj1953 Normal

根据期望的线性性,答案就是 ∑ \sum 每个连通块出现次数的期望
而每个连通块次数的期望就是 ∑ \sum 连通块的根与每个点连通次数的期望
也就是对于一条路径 ( i , j ) (i,j) (i,j),设 i i i 为根,那么 i i i 必须是这条路径第一个被选择的点,概率为 1 d i s ( i , j ) \frac{1}{dis(i,j)} dis(i,j)1,其中 d i s ( i , j ) dis(i,j) dis(i,j) 表示 ( i , j ) (i,j) (i,j) 上的点数
那么只要统计不同的 d i s ( i , j ) dis(i,j) dis(i,j) 的个数即可
直接点分治+ F F T FFT FFT

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn(1 << 18);
const int mod(998244353);

inline void Inc(int &x, int y) {
	x = x + y >= mod ? x + y - mod : x + y;
}

inline void Dec(int &x, int y) {
	x = x - y < 0 ? x - y + mod : x - y;
}

inline int Add(int x, int y) {
	return x + y >= mod ? x + y - mod : x + y;
}

inline int Sub(int x, int y) {
	return x - y < 0 ? x - y + mod : x - y;
}

inline int Pow(ll x, int y) {
	register ll ret = 1;
	for (; y; y >>= 1, x = x * x % mod)
		if (y & 1) ret = ret * x % mod;
	return ret;
}

int w[2][maxn], a[maxn], b[maxn], len, l, r[maxn];

inline void Init(int n) {
	int i, x, y;
	for (l = 0, len = 1; len < n; len <<= 1) ++l;
	for (i = 0; i < len; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
	x = Pow(3, (mod - 1) / len), y = Pow(x, mod - 2), w[0][0] = w[1][0] = 1;
	for (i = 1; i < len; ++i) w[0][i] = (ll)w[0][i - 1] * x % mod, w[1][i] = (ll)w[1][i - 1] * y % mod;
	for (i = 0; i < len; ++i) a[i] = b[i] = 0;
}

inline void DFT(int *p, int opt) {
	int i, j, k, x, y, t;
	for (i = 0; i < len; ++i) if (i < r[i]) swap(p[i], p[r[i]]);
	for (i = 1; i < len; i <<= 1)
		for (j = 0, t = i << 1; j < len; j += t)
			for (k = 0; k < i; ++k) {
				x = p[j + k], y = (ll)w[opt == -1][len / t * k] * p[j + k + i] % mod;
				p[j + k] = Add(x, y), p[j + k + i] = Sub(x, y);
			}
	if (opt == -1) for (i = 0, t = Pow(len, mod - 2); i < len; ++i) p[i] = (ll)p[i] * t % mod;
}

int n, first[maxn], cnt, cur[maxn], size[maxn], mx[maxn], vis[maxn], rt, sz, deep[maxn], mxd;
long double ans;

struct Edge {
	int to, next;
} edge[maxn << 1];

inline void AddEdge(int u, int v) {
	edge[cnt] = (Edge){v, first[u]}, first[u] = cnt++;
	edge[cnt] = (Edge){u, first[v]}, first[v] = cnt++;
}

void Getroot(int u, int ff) {
	int e, v;
	size[u] = 1, mx[u] = 0;
	for (e = first[u]; ~e; e = edge[e].next)
		if ((v = edge[e].to) != ff && !vis[v]) {
			Getroot(v, u);
			size[u] += size[v];
			mx[u] = max(mx[u], size[v]);
		}
	mx[u] = max(mx[u], sz - size[u]);
	if (mx[u] < mx[rt]) rt = u;
}

void Getdeep(int u, int ff, int d) {
	int e, v;
	++deep[d], mxd = max(mxd, d);
	for (e = first[u]; ~e; e = edge[e].next)
		if ((v = edge[e].to) != ff && !vis[v]) Getdeep(v, u, d + 1);
}

void Solve(int u) {
	int e, v, i, len1, len2;
	mxd = 0, Getdeep(u, 0, 0), vis[u] = 1;
	Init((mxd << 1) + 1), len1 = mxd << 1;
	for (i = 0; i <= mxd; ++i) a[i] = deep[i], deep[i] = 0;
	DFT(a, 1), len2 = len;
	for (i = 0; i < len2; ++i) a[i] = (ll)a[i] * a[i] % mod;
	DFT(a, -1);
	for (i = 0; i <= len1; ++i) Inc(cur[i + 1], a[i]);
	for (e = first[u]; ~e; e = edge[e].next)
		if (!vis[v = edge[e].to]) {
			mxd = 0, Getdeep(v, u, 1), Init(mxd << 1 | 1), len1 = mxd << 1;
			for (i = 0; i <= mxd; ++i) b[i] = deep[i], deep[i] = 0;
			DFT(b, 1);
			for (i = 0; i < len; ++i) b[i] = (ll)b[i] * b[i] % mod;
			DFT(b, -1);
			for (i = 0; i <= len1; ++i) Dec(cur[i + 1], b[i]);
		}
	for (e = first[u]; ~e; e = edge[e].next)
		if (!vis[v = edge[e].to]) {
			rt = 0, sz = size[v];
			Getroot(v, u), Solve(rt);
		}
}

int main() {
	int i, j, k, u, v;
	memset(first, -1, sizeof(first));
	scanf("%d", &n);
	for (i = 1; i < n; ++i) scanf("%d%d", &u, &v), AddEdge(u + 1, v + 1);
	sz = n, mx[0] = n + 1, Getroot(1, 0), Solve(rt);
	for (i = 1; i <= n; ++i) if (cur[i]) ans += 1.0 * cur[i] / (1.0 * i);
	printf("%.4Lf\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值