1. 程式人生 > >BZOJ3451 Tyvj1953 Normal 【期望 + 點分治 + NTT】

BZOJ3451 Tyvj1953 Normal 【期望 + 點分治 + NTT】

否則 display while ace || cpp AR pro 概率

題目鏈接

BZOJ3451

題解

考慮每個點產生的貢獻,即為該點在點分樹中的深度期望值
由於期望的線性,最後的答案就是每個點貢獻之和

對於點對\((i,j)\),考慮\(j\)成為\(i\)祖先的概率,記為\(P(i,j)\)
那麽
\[ans = \sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{n} P(i,j)\]
由於是隨機選點,\(i\)\(j\)路徑上所有點第一個被選中的除非是\(j\),否則\(j\)就不是\(i\)的祖先
由於是隨機的,所以\(P(i,j) = \frac{1}{dis(i,j)}\)
綜上
\[ans = \sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{n} \frac{1}{dis(i,j)}\]

為了方便計算,我們可以枚舉\(dis\),計算有多少個長度為\(dis\)的點對
直接枚舉 + 點分是\(O(n^2logn)\)的,我們考慮能不能一起算
當然可以,兩個子樹之間的貢獻合並實際上就是一個生成函數乘積

我們對於一棵分治樹,先求出整棵樹各個深度數量數列形成的生成函數,平方一次
由於會包含回到同一個子樹的情況,在向子樹求一遍減去即可

這樣就優化成了\(O(nlog^2n)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map> #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define mp(a,b) make_pair<int,int>(a,b) #define cls(s) memset(s,0,sizeof(s)) #define cp pair<int,int> #define LL long long int using namespace std; const int maxn = 150005
,maxm = 100005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int n,h[maxn],ne = 1; struct EDGE{int to,nxt;}ed[maxn]; inline void build(int u,int v){ ed[++ne] = (EDGE){v,h[u]}; h[u] = ne; ed[++ne] = (EDGE){u,h[v]}; h[v] = ne; } const int G = 3,P = 998244353; int R[maxn]; inline int qpow(int a,int b){ int re = 1; for (; b; b >>= 1,a = 1ll * a * a % P) if (b & 1) re = 1ll * re * a % P; return re; } void NTT(int* a,int n,int f){ for (int i = 0; i < n; i++) if (i < R[i]) swap(a[i],a[R[i]]); for (int i = 1; i < n; i <<= 1){ int gn = qpow(G,(P - 1) / (i << 1)); for (int j = 0; j < n; j += (i << 1)){ int g = 1,x,y; for (int k = 0; k < i; k++,g = 1ll * g * gn % P){ x = a[j + k],y = 1ll * g * a[j + k + i] % P; a[j + k] = (x + y) % P,a[j + k + i] = ((x - y) % P + P) % P; } } } if (f == 1) return; int nv = qpow(n,P - 2); reverse(a + 1,a + n); for (int i = 0; i < n; i++) a[i] = 1ll * a[i] * nv % P; } LL ans[maxn]; int F[maxn],fa[maxn],siz[maxn],vis[maxn],N,rt; void getrt(int u){ siz[u] = 1; F[u] = 0; Redge(u) if (!vis[to = ed[k].to] && to != fa[u]){ fa[to] = u; getrt(to); siz[u] += siz[to]; F[u] = max(F[u],siz[to]); } F[u] = max(F[u],N - siz[u]); if (F[u] < F[rt]) rt = u; } int dep[maxn],md; int A[maxn],B[maxn]; void dfs(int u){ A[dep[u]]++; siz[u] = 1; md = max(md,dep[u]); Redge(u) if (!vis[to = ed[k].to] && to != fa[u]){ fa[to] = u; dep[to] = dep[u] + 1; dfs(to); siz[u] += siz[to]; } } void dfs1(int u){ B[dep[u]]++; md = max(md,dep[u]); Redge(u) if (!vis[to = ed[k].to] && to != fa[u]) dfs1(to); } void solve(int u){ vis[u] = true; siz[u] = N; fa[u] = 0; for (int i = 0; i <= N; i++) A[i] = B[i] = 0; dep[u] = 0; A[0] = 1; md = 0; Redge(u) if (!vis[to = ed[k].to]){ fa[to] = u; dep[to] = 1; dfs(to); } int m = (md << 1),L = 0,n = 1; while (n <= m) n <<= 1,L++; for (int i = 1; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1)); for (int i = md + 1; i < n; i++) A[i] = 0; NTT(A,n,1); for (int i = 0; i < n; i++) A[i] = 1ll * A[i] * A[i] % P; NTT(A,n,-1); for (int i = 0; i < n; i++) ans[i + 1] += 1ll * A[i]; Redge(u) if (!vis[to = ed[k].to]){ md = 1; dfs1(to); m = (md << 1),L = 0,n = 1; while (n <= m) n <<= 1,L++; for (int i = 1; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1)); NTT(B,n,1); for (int i = 0; i < n; i++) B[i] = 1ll * B[i] * B[i] % P; NTT(B,n,-1); for (int i = 0; i < n; i++) ans[i + 1] -= 1ll * B[i]; for (int i = 0; i < n; i++) B[i] = 0; } Redge(u) if (!vis[to = ed[k].to]){ N = siz[to]; F[rt = 0] = INF; getrt(to); solve(rt); } } int main(){ n = read(); for (int i = 1; i < n; i++) build(read() + 1,read() + 1); F[rt = 0] = INF; N = n; getrt(1); solve(rt); double Ans = 0; //REP(i,n) printf("dis %d cnt %lld\n",i,ans[i]); for (int i = 1; i <= n; i++) Ans += 1.0 / i * ans[i]; printf("%.4lf\n",Ans); return 0; }

BZOJ3451 Tyvj1953 Normal 【期望 + 點分治 + NTT】