「UOJ351」新年的叶子

题目描述

有一棵大小为 \(n\) 的树,每次随机将一个叶子染黑,可以重复染,问期望染多少次后树的直径会缩小。

\(1 \leq n \leq 5 \times 10^5\)

解题思路 :

首先要利用一个经典的结论,树的所有直径的中心为同一个点/边。不妨给每条边加一个虚拟点,这样整颗树的直径就只会交于同一个点了。

接下来考虑树的直径是由中心的两个儿子的两个深度为 \(maxdep\) 的叶子构成的,所以问题等价于将叶子根据中心的儿子分成若干个集合,对于所有染色方案求染到只剩一个集合没有被完全染黑的期望步数之和,这个东西再除以一个方案数就是答案。

这个东西好难求啊,推了半天式子还是不太会,最后只能看题解辅助推导 \(\text{qwq}\) 。首先把随机染黑一个叶子转化为随机染黑一个没有染黑的叶子,那么此时的期望步数就是总叶子数除以没有被染黑的叶子数。然后枚举一下最后一个被染黑的集合是哪个集合,在第一次到达只有它没被完全染黑这个状态之前其被染黑的叶子总数是多少,不妨分别设他们为 \(i, k\) 。那么首先先选出 \(k\) 个叶子染黑,其在操作序列中有 \(n - sz_i+k-1\) 个位置可以放,\(-1\) 是因为不能放最后一个位置,如果放了最后一个位置其就不是第一次到达的状态就算重了,然后把其它的操作的排列数乘上就是方案数。而步数就是 \(\sum_{i=sz_i-k+1}^{n} \frac{m}{i}\) ,其中 \(n\) 是集合总大小,\(m\) 是总的叶子数量。 由于我的写法常数太大,在 UOJ 上的统计榜成功吃鸡。

/*program by mangoyang*/
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
} #define pii pair<int, int>
#define fi first
#define se second const int N = 1000005, mod = 998244353; vector<int> g[N];
int dep[N], f[N], js[N], inv[N], buf[N], n, m, all, mxdep, rt;
ll h[N], ans;
namespace prework{
queue<pii> q; int vis[N], pre[N];
inline pii bfs(int s){
memset(vis, 0, sizeof(vis));
q.push(make_pair(s, 0)), vis[s] = 1; pii res;
while(!q.empty()){
pii now = q.front(); q.pop();
int x = now.fi, dis = now.se; res = now;
for(int i = 0; i < g[x].size(); i++){
int v = g[x][i];
if(vis[v]) continue;
vis[v] = 1, pre[v] = x;
q.push(make_pair(v, dis + 1));
}
}
return res;
}
inline void dfs(int u, int fa){
dep[u] = dep[fa] + 1, f[u] = dep[u], buf[u] = 1;
for(int i = 0; i < g[u].size(); i++){
int v = g[u][i];
if(v == fa) continue;
dfs(v, u);
if(f[v] > f[u]) buf[u] = buf[v], f[u] = f[v];
else if(f[v] == f[u]) buf[u] += buf[v];
}
if(g[u].size() == 1 && fa) m++;
}
inline void realmain(){
pii s1 = bfs(1), s2 = bfs(s1.fi);
int dis = s2.se / 2; rt = s2.fi;
for(int i = 1; i <= dis; i++) rt = pre[rt];
dfs(rt, 0);
}
} inline void up(ll &x, int y){ (x += y) %= mod; }
inline int Pow(int a, int b){
int ans = 1;
for(; b; b >>= 1, a = 1ll * a * a % mod)
if(b & 1) ans = 1ll * ans * a % mod;
return ans; }
inline int C(int x, int y){
return 1ll * js[x] * inv[y] % mod * inv[x-y] % mod;
}
inline int calc(int x, int y){
ll res = 1ll * C(y, x) * C(all-(y-x)-1, x) % mod;
(res *= (1ll * js[y-x] * js[all-y] % mod)) %= mod;
(res *= (1ll * js[x] * (h[all] - h[y-x]) % mod)) %= mod;
return res;
} int main(){
js[0] = inv[0] = 1;
for(int i = 1; i < N; i++)
js[i] = 1ll * js[i-1] * i % mod, inv[i] = Pow(js[i], mod - 2);
read(n); int size = n;
for(int i = 1, x, y; i < n; i++){
read(x), read(y), ++size;
g[x].push_back(size), g[size].push_back(x);
g[y].push_back(size), g[size].push_back(y);
}
prework::realmain();
for(int i = 1; i <= m; i++)
up(h[i], 1ll * (h[i-1] + 1ll * m * Pow(i, mod - 2)) % mod);
for(int i = 0; i < g[rt].size(); i++) mxdep = max(mxdep, f[g[rt][i]]);
for(int i = 0; i < g[rt].size(); i++){
if(f[g[rt][i]] == mxdep) all += buf[g[rt][i]];
else buf[g[rt][i]] = 0;
}
for(int i = 0; i < g[rt].size(); i++)
for(int j = 0; j < buf[g[rt][i]]; j++) up(ans, calc(j, buf[g[rt][i]]));
cout << (1ll * ans * inv[all] % mod + mod) % mod;
return 0;
}

「UOJ351」新年的叶子的更多相关文章

  1. 「TJOI2015」概率论 解题报告

    「TJOI2015」概率论 令\(f_i\)代表\(i\)个点树形态数量,\(g_i\)代表\(i\)个点叶子个数 然后列一个dp \[ f_i=\sum_{j=0}^{i-1} f_j f_{i-j ...

  2. loj#2009.「SCOI2015」小凸玩密室

    题目链接 loj#2009. 「SCOI2015」小凸玩密室 题解 树高不会很高<=20 点亮灯泡x,点亮x的一个子树,再点亮x另外的子树, 然后回到x的父节点,点亮父节点之后再点亮父节点的其他 ...

  3. 【LibreOJ】#6395. 「THUPC2018」城市地铁规划 / City 背包DP+Prufer序

    [题目]#6395. 「THUPC2018」城市地铁规划 / City [题意]给定n个点要求构造一棵树,每个点的价值是一个关于点度的k次多项式,系数均为给定的\(a_0,...a_k\),求最大价值 ...

  4. LOJ #2585. 「APIO2018」新家

    #2585. 「APIO2018」新家 https://loj.ac/problem/2585 分析: 线段树+二分. 首先看怎样数颜色,正常的时候,离线扫一遍右端点,每次只记录最右边的点,然后查询左 ...

  5. Loj #2568. 「APIO2016」烟花表演

    Loj #2568. 「APIO2016」烟花表演 题目描述 烟花表演是最引人注目的节日活动之一.在表演中,所有的烟花必须同时爆炸.为了确保安全,烟花被安置在远离开关的位置上,通过一些导火索与开关相连 ...

  6. Loj #3044. 「ZJOI2019」Minimax 搜索

    Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...

  7. Loj #2570. 「ZJOI2017」线段树

    Loj #2570. 「ZJOI2017」线段树 题目描述 线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构.优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质. 最近可怜 ...

  8. 「APIO2016」烟花表演

    「APIO2016」烟花表演 解题思路 又是一道 solpe trick 题,观察出图像变化后不找一些性质还是挺难做的. 首先令 \(dp[u][i]\) 为节点 \(u\) 极其子树所有叶子到 \( ...

  9. 「NOI2012」迷失游乐园

    「NOI2012」迷失游乐园 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该 ...

随机推荐

  1. HOMEWORK-2

    没什么超乎常人的技能吧,我想.关于C的学习之前一直是自学,上了大学也是吃老底(上一篇提到了),因为这个学期一直在学matlab,C除了帮人写过作业教过课自己也没写点什么. 指针的概念还算清楚,毕竟经常 ...

  2. select多选框

    select多选框 效果: 代码: <HTML> <HEAD> <TITLE>选择下拉菜单</TITLE> <meta http-equiv=&q ...

  3. Sublime之插件的安装(一)

    由于最近刚换了一个工作,所以决定重新申请一个blog,把工作当中遇到的一些问题记录下来,方便自己下次忘记,也希望能与一起需要的小伙伴一起共勉. 如果有不同的观点或者是不同的看法,大家都可以畅谈,我一直 ...

  4. Vue 传递

    今天刷了一遍Vue的API,做个小笔记 父子传递数据时,父组件里标记要传的数据,子组件里用props获取,子组件用$emit('func',args)发布事件,父组件用@func接收. 方法一 par ...

  5. PHP中的 get_magic_quotes_runtime

    get_magic_quotes_runtime() 获得外部文件及数据库资料时是否进行转义 set_magic_quotes_runtime(1); 临时设置获得外部文件及数据库资料时是否进行转义 ...

  6. kippo蜜罐搭建

    kippo蜜罐搭建 总结一下kippo蜜罐搭建的方法,centos系统.kippo-master.zip的安装包 (gcc,python-devel,pip,twisted==13.10,pycryp ...

  7. 深入解析Mysql 主从同步延迟原理及解决方案

    MySQL的主从同步是一个很成熟的架构,优点为:①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力;②在从主服务器进行备份,避免备份期间影响主服务器服务;③当主服务器出现问题时,可以 ...

  8. [转载]锁无关的(Lock-Free)数据结构

    锁无关的(Lock-Free)数据结构 在避免死锁的同时确保线程继续 Andrei Alexandrescu 刘未鹏 译 Andrei Alexandrescu是华盛顿大学计算机科学系的在读研究生,也 ...

  9. HTTP 请求 的方法Util

    HTTP请求 的一系列方法总结 /** * *******************************传统请求--开始************************************** ...

  10. fsarchiver创建系统镜像(dd命令也可以)

    fsarchiver简介 fsarchiver可以将整个文件系统的内容保存成一个压缩形式的归档文件,包含文件系统本身.所以用来做系统镜像是一个不错的选择,一旦系统崩溃但可以进入救援模式,我们就可以使用 ...