UOJ#351-新年的葉子【樹的直徑,數學期望】
阿新 • • 發佈:2021-12-01
正題
題目連結:https://uoj.ac/problem/351
題目大意
給出\(n\)個點的一棵樹,開始所有點都是白色,每次隨機點黑一個葉子(可以重複點),求期望多少次能使得白色點構成的圖直徑發生變化。
答案對\(998244353\)取模
\(1\leq n\leq 5\times 10^5\)
解題思路
考慮什麼時候會直徑會產生變化。
假設直徑的長度\(L\)為偶數,那麼所有的直徑都有一個共同的中心點,設為\(x\)。此時我們需要在\(x\)的兩棵子樹中各自找到兩個深度為\(\frac L 2\)的葉子,那麼就可以組成一條直徑。
換句話說,把所有深度為\(\frac L 2\)葉子取出來,然後把它們按照在那個根的子樹中分成若干個集合。然後當我們染色到只有一個集合沒有全部染色的時候就結束了。
那麼現在問題變成給出若干個集合和一些集合外的點,每次染一個點,求期望多少次能夠染成只有一個集合沒有全部染色。
考慮總共有\(n\)個點,有\(i\)個已經染色了,那麼染色下任意一個的概率就是\(\frac{i}{n}\),期望就是\(\frac{n}{i}\)。
預處理\(f_i=\sum_{j=1}^i\frac{n}{j}\),然後我們可以考慮把集合中的點排列然後按順序染,最後除上方案就好了。
假設所有集合中總共有\(m\)個點,目前列舉到的集合有\(k\)個點,然後染到這個集合剩下\(p\)個點的時候其他集合都染完了,那麼期望就是
\[\frac{1}{m!}\binom{k}{p}\times \left(\ (m-p)!-(k-p)(m-p-1)!\ \right)\times p!\times (f_m-f_p) \](中間的減法是為了保證最後剩下的\(p\)
至於直徑長度是奇數的情況,那麼有兩個中心點,也就是有一條中心邊,分成兩個集合按照上面的搞就好了。
時間複雜度:\(O(n)\)
code
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=5e5+10,P=998244353; struct node{ ll to,next; }a[N<<1]; ll n,m,cnt,mxdis,root,tot,ans; ll ls[N],v[N],pre[N],fac[N],inv[N],f[N]; void addl(ll x,ll y){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;return; } void findL(ll x,ll fa,ll dis){ if(dis>mxdis)mxdis=dis,root=x; for(ll i=ls[x];i;i=a[i].next){ ll y=a[i].to; if(y==fa)continue; pre[y]=x;findL(y,x,dis+1); } return; } void markL(ll x,ll fa,ll dis,ll k){ if(dis==mxdis/2&&(!a[ls[x]].next))v[k]++; for(ll i=ls[x];i;i=a[i].next){ ll y=a[i].to; if(y==fa)continue; markL(y,x,dis+1,k); } return; } ll C(ll n,ll m) {return fac[n]*inv[m]%P*inv[n-m]%P;} signed main() { scanf("%lld",&n); for(ll i=1,x,y;i<n;i++){ scanf("%lld%lld",&x,&y); addl(x,y);addl(y,x); } ll k=0; for(ll i=1;i<=n;i++)k+=!(a[ls[i]].next); inv[0]=inv[1]=fac[0]=1; for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P; for(ll i=1;i<N;i++)f[i]=(f[i-1]+k*inv[i]%P)%P; for(ll i=1;i<N;i++)fac[i]=fac[i-1]*i%P,inv[i]=inv[i-1]*inv[i]%P; findL(1,0,0);mxdis=0; findL(root,0,0); if(mxdis&1){ ll x=root; for(ll i=1;i<=mxdis/2;i++)x=pre[x]; ll y=pre[x];markL(x,y,0,1);markL(y,x,0,2); cnt=2; } else{ ll x=root; for(ll i=1;i<=mxdis/2;i++)x=pre[x]; for(ll i=ls[x];i;i=a[i].next) cnt++,markL(a[i].to,x,1,cnt); } for(ll i=1;i<=cnt;i++)m+=v[i]; for(ll i=1;i<=cnt;i++) for(ll j=1;j<=v[i];j++){ ll w=(f[m]-f[j]+P)%P; w=(fac[m-j]-(v[i]-j)*fac[m-j-1]%P+P)%P*fac[j]%P*w%P; (ans+=w*inv[m]%P*C(v[i],j)%P)%=P; } printf("%lld\n",ans); return 0; }