UOJ#351-新年的叶子【树的直径,数学期望】
正題
題目鏈接:https://uoj.ac/problem/351
題目大意
給出nnn個點的一棵樹,開始所有點都是白色,每次隨機點黑一個葉子(可以重復點),求期望多少次能使得白色點構成的圖直徑發生變化。
答案對998244353998244353998244353取模
1≤n≤5×1051\leq n\leq 5\times 10^51≤n≤5×105
解題思路
考慮什么時候會直徑會產生變化。
假設直徑的長度LLL為偶數,那么所有的直徑都有一個共同的中心點,設為xxx。此時我們需要在xxx的兩棵子樹中各自找到兩個深度為L2\frac L 22L?的葉子,那么就可以組成一條直徑。
換句話說,把所有深度為L2\frac L 22L?葉子取出來,然后把它們按照在那個根的子樹中分成若干個集合。然后當我們染色到只有一個集合沒有全部染色的時候就結束了。
那么現在問題變成給出若干個集合和一些集合外的點,每次染一個點,求期望多少次能夠染成只有一個集合沒有全部染色。
考慮總共有nnn個點,有iii個已經染色了,那么染色下任意一個的概率就是in\frac{i}{n}ni?,期望就是ni\frac{n}{i}in?。
預處理fi=∑j=1injf_i=\sum_{j=1}^i\frac{n}{j}fi?=∑j=1i?jn?,然后我們可以考慮把集合中的點排列然后按順序染,最后除上方案就好了。
假設所有集合中總共有mmm個點,目前枚舉到的集合有kkk個點,然后染到這個集合剩下ppp個點的時候其他集合都染完了,那么期望就是
1m!(kp)×((m?p)!?(k?p)(m?p?1)!)×p!×(fm?fp)\frac{1}{m!}\binom{k}{p}\times \left(\ (m-p)!-(k-p)(m-p-1)!\ \right)\times p!\times (f_m-f_p)m!1?(pk?)×(?(m?p)!?(k?p)(m?p?1)!?)×p!×(fm??fp?)
(中間的減法是為了保證最后剩下的ppp個點前面一定是一個不是枚舉集合中的點,然后fm?fpf_m-f_pfm??fp?是因為我們假設不在集合中的點已經染色了,那么剩下需要染m?pm-pm?p個)。
至于直徑長度是奇數的情況,那么有兩個中心點,也就是有一條中心邊,分成兩個集合按照上面的搞就好了。
時間復雜度:O(n)O(n)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; }總結
以上是生活随笔為你收集整理的UOJ#351-新年的叶子【树的直径,数学期望】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单夏至祝福语
- 下一篇: P5643-[PKUWC2018]随机游