jzoj5954 【NOIP2018模擬11.5A組】走向巔峰(直徑性質,期望)
阿新 • • 發佈:2018-12-02
Description
眾所周知,DH是一位人生贏家,他不僅能虐暴全場,而且還正在走向人生巔峰;
在巔峰之路上,他碰到了這一題:
給出一棵n個節點的樹,我們每次隨機染黑一個葉子節點(可以重複染黑),操作無限次後,這棵樹的所有葉子節點必然全部會被染成黑色。
定義R為這棵樹不經過黑點的直徑,求使R第一次變小期望的步數。
對於100%的資料,滿足n<=5*10^5。
分析
有一個性質: 樹上所有直徑的交集當長度是偶數時是一個點,奇數時是一條邊,而且在中點。
以中心為根,求出每個點的dep。 顯然只有dep為R / 2的葉子對答案有影響。
接下來相當於,有k個集合,每個集合有ai個點。每次有p的概率隨機刪一個點,求刪剩下一個集合的期望步數。(無效點已經被刪除成功的概率考慮了)
按照LeFee的方法,可以列舉某個集合最後剩下
個,然後求期望。
然而PTY有一種更簡單的方法。先列舉一個集合x,假裝刪他們是失敗的,然後算出將其他集合的點刪完的期望步數。這樣多考慮的一部分是先把集合x全刪完,再刪完其他集合的貢獻,並且這個步數就是把所有點刪完的步數。
可以發現,對於每一種確定的方案,除了最後剩下的那個集合,其他集合都多算了這種方案。將最終答案減去 即可。
死因: 不知道結論
實現
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5e5 + 10, mo = 998244353;
int n;
int final[N],nex[N*2],to[N*2],tot,fa[N],dep[N],deg[N];
void link(int x,int y) {
to[++tot] = y, nex[tot] = final[x], final[x] = tot;
deg[x]++;
}
int lcnt;
void dfs(int x){
for (int i = final[x]; i; i=nex[i]) {
int y = to[i]; if (y != fa[x]) {
fa[y] = x; dfs(y);
}
}
lcnt += deg[x] == 1;
}
int R,mid,Q[N],dis[N];
int find(int src,int &far) {
int h = 0, t = 0, ret = 0;
memset(dis,127,sizeof dis);
dis[src] = 0;
Q[++t] = src;
while (h < t) {
int x = Q[++h];
for (int i = final[x]; i; i=nex[i]) {
int y = to[i];
if (dis[y] > dis[x] + 1) {
dis[y] = dis[x] + 1;
Q[++t] = y;
}
}
}
far = Q[t];
return dis[Q[t]];
}
int s1,s2,tg,S[N];
void fdr(int x,int from) {
S[++S[0]] = x;
if (x == tg) {
if (R & 1) {
s1 = S[R / 2 + 1], s2 = S[R / 2 + 2];
} else {
s1 = S[R / 2 + 1];
}
return;
}
for (int i = final[x]; i; i=nex[i]) {
int y = to[i]; if (y != from) {
fdr(y, x);
}
}
S[0]--;
}
int s[N],sz[N];
void findset(int x,int from) {
for (int i = final[x]; i; i=nex[i]) {
int y = to[i]; if (y != from) {
dep[y] = dep[x] + 1;
findset(y, x);
sz[x] += sz[y];
if (x == s1 || x == s2) {
if (!(R & 1)) {
s[++s[0]] = sz[y] ;
}
}
}
}
sz[x] += dep[x] == R / 2;
}
void findset() {
int a,b;
find(1,a);
R = find(a,b);
tg = b, fdr(a, 0);
if (R & 1) {
findset(s1, s2);
findset(s2, s1);
s[0] = 2; s[1] = sz[s1], s[2] = sz[s2];
} else
findset(s1, 0);
}
typedef long long ll;
ll ksm(ll x,ll y) {
ll ret = 1; for (; y; y>>=1) {
if (y & 1) ret = ret * x % mo;
x = x * x % mo;
}
return ret;
}
ll sum[N];
ll ans;
void solve() {
for (int i = 1; i <= n; i++) sum[i] = (sum[i - 1] + lcnt * ksm(i, mo - 2)) % mo;
ll d0 = 0;
for (int i = 1; i <= s[0]; i++) d0 += s[i];
for (int i = 1; i <= s[0]; i++) {
ans = (ans + sum[d0 - s[i]]) % mo;
}
ans = (ans - (s[0] - 1) * sum[d0] % mo) % mo;
}
int main() {
freopen("winer.in","r",stdin);
freopen("winer.out","w",stdout);
cin>>n;
for (int i = 1; i < n; i++) {
int u,v; scanf("%d %d",&u,&v);
link(u,v), link(v,u);
}
dfs(1);
findset();
solve();
cout<<(ans+mo)%mo<<endl;
}