题意:
众所周知,DH是一位人生赢家,他不仅能虐暴全场,而且还正在走向人生巅峰;
在巅峰之路上,他碰到了这一题:
给出一棵个节点的树,我们每次随机染黑一个叶子节点(可以重复染黑),操作无限次后,这棵树的所有叶子节点必然全部会被染成黑色。
定义为这棵树不经过黑点的直径,求使第一次变小期望的步数。
数据范围:
对于%的数据,满足;
对于%的数据,满足;
另外有%数据,满足树为菊花图;
另外有%数据,满足每个点度数不超过;
对于%的数据,满足。
Analysis:
据我所见,这是一道极其玄学的题目:
首先我们考虑用直径性质分析一波,对于所有直径,我们可以找到一个必经点,或必经边。
我们以其为根,然后建树,端点以所属的根的子树分类,会划分成若干个集合,想要让其变小,那么最后只会剩下一个集合里面有点没被染黑。
我们可以先知道一个值,若当前有个点没被染黑,总点数为,那么再染黑一个点的期望步数为:。
一种比较麻烦的方法是:
我们可以先把有用的叶子找出来,也就是为直径端点的叶子,这些叶子的深度在新树中为,为直径长度。那么其它叶子就没用了,我们视为已经删去。设集合大小为直径端点叶子数。
第二种是老曹提供的比较简单的方法:
我们每一次枚举一个集合,使它最后删,那么对答案贡献为:。但是这样可能会出现不合法情况,即枚举的集合在其他集合删完前被删完了,那么此时贡献的是全部被染黑的贡献,这种情况会被算集合个数次,减去即可。
复杂度。我写了第一种方法。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 5e5 + 5;
const int mo = 998244353;
typedef long long ll;
int st[N],to[N << 1],nx[N << 1],vis[N];
int fac[N],fac_[N],inv[N],sta[N],num[N],fa[N];
int n,m,tot,len,all,z;
inline void add(int u,int v)
{
to[++tot] = v,nx[tot] = st[u],st[u] = tot;
to[++tot] = u,nx[tot] = st[v],st[v] = tot;
}
inline int C(int n,int m) { return (ll)fac[n] * fac_[m] % mo * fac_[n - m] % mo; }
inline void dfs(int x,int s)
{
if (s > len) len = s,z = x;
for (int i = st[x] ; i ; i = nx[i])
if (to[i] != fa[x]) fa[to[i]] = x,dfs(to[i],s + 1);
}
inline void dfs1(int x,int fr,int d,int g)
{
if (d == len / 2 && vis[x]) ++num[g];
for (int i = st[x] ; i ; i = nx[i])
if (to[i] != fr) dfs1(to[i],x,d + 1,g);
}
int main()
{
freopen("winer.in","r",stdin);
freopen("winer.out","w",stdout);
scanf("%d",&n),inv[1] = fac[0] = fac_[0] = 1;
for (int i = 1 ; i <= n ; ++i)
{
if (i > 1) inv[i] = (ll)(mo - mo / i) * inv[mo % i] % mo;
fac[i] = (ll)fac[i - 1] * i % mo,fac_[i] = (ll)fac_[i - 1] * inv[i] % mo;
}
for (int i = 1 ; i < n ; ++i)
{
int u,v; scanf("%d%d",&u,&v);
add(u,v);
}
for (int i = 1 ; i <= n ; ++i) if (!nx[st[i]]) vis[i] = 1,++m;
dfs(1,0);
int fir = z; fa[fir] = len = 0,dfs(fir,0);
int sec = z; len = -1;
while (sec != fir) sta[++len] = sec,sec = fa[sec];
sta[++len] = fir;
if (len & 1)
{
dfs1(sta[len / 2],sta[len / 2 + 1],0,++all);
dfs1(sta[len / 2 + 1],sta[len / 2],0,++all);
}
else
{
int rt = sta[len / 2];
for (int i = st[rt] ; i ; i = nx[i]) dfs1(to[i],rt,1,++all);
}
int sum = 0,ans = 0;
for (int i = 1 ; i <= all ; ++i) sum += num[i];
inv[sum + 1] = 0;
for (int i = sum ; i ; --i) inv[i] = ((ll)inv[i] * m % mo + inv[i + 1]) % mo;
for (int i = 1 ; i <= all ; ++i)
for (int j = 0 ; j < num[i] ; ++j)
ans = (ll)(ans + (ll)C(num[i],j) * fac[sum - (num[i] - j + 1)] % mo * (sum - num[i]) % mo * inv[num[i] - j + 1] % mo * fac[num[i] - j] % mo) % mo;
printf("%d\n",(ll)ans * fac_[sum] % mo);
return 0;
}