仓鼠找sugar II

题目描述
小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a,是任意的)他的基友卧室(b,还是任意的)。(注意,a有可能等于b。)然而小仓鼠学OI学傻了,不知道怎么怎么样才能最短的走到目的地。于是他只能随便乱走。当他在每一个节点时,等概率到这个点的母亲或者所有孩子节点(例如这个节点有一个母亲节点和两个子节点,那么下一步走到这3个节点的概率都是1/3)。一但走到了他基友的卧室,就会停下。
现在小仓鼠希望知道,他走到目的地时,走的步数的期望。这个期望本来是一个有理数,但是为了避免误差,我们要求对这个有理数取模,%998244353。
下面是“分数”模运算的定义:
b, m互质
k = a/b (mod m) <=> kb = a (mod m)
这里求 x = 1/17 (mod 2668)
<=>
17x = 1 (mod 2668)
<=>
17x = 2668k + 1 (k∈整数)
取合适的k使得17|(2668k+1) 这里刚好17 | (2668 + 1)
所以k = 1, x = (2668+1)/17 = 157
当然,当k = 1 + 17n 时,
x = (2668 + 17·n·2668 + 1)/17 = 157 + 2668n
也符合条件(n任意整数)
但如果限定 2668 > x > 0,x是唯一的。
小仓鼠那么弱,还要天天被JOHNKRAM大爷虐,请你快来救救他吧!
输入输出格式
输入格式:
第一行一个正整数n,表示这棵树节点的个数。
接下来n-1行,每行两个正整数u和v,表示节点u到节点v之间有一条边。
输出格式:
一个整数,表示取模后的答案。
输入输出样例
输入样例#1:
3
1 2
1 3
输出样例#1:
110916041
说明
对于30%的数据 n<=5;
对于50%的数据 n<=5000;
对于所有数据 n<=100000。
样例解释
期望是16/9
如果a在叶子 b在根,E1=1。有2种情况。
如果a在根,b在叶子。E2=1/2+3*1/4+5*1/8…=3。有2种情况。
【仓鼠找sugar II】如果a和b都在不同的叶子,E3=E2+1。有2种情况。
如果a=b,E4=0,有3种情况。
所以期望是16/9,有理数取模后就是输出。
设f[u->v]表示u、v之间有边的情况下,从u到v的期望距离。考虑从u出发第一步走到哪里可以列出方程f[u->v]=1/d[u]+sum{(1+f[k->u]+f[u->v])/d[u]}(u、k之间有边且k!=v),d[u]表示u的度数。解方程得f[u->v]=d[u]+sum{f[k->u]}(u、k之间有边且k!=v)。把f[k->u]一层层代入,可得f[u->v]=sum{d[i]}(i在以v为根时u的子树内)。考虑每条边被计算了多少次,化简得f[u->v]=2siz[v][u]-1,siz[v][u]表示以v为根时u的子树大小。只需要计算出所有的siz[1][i],那么判断一下u和v谁是父亲就可以O(1)的计算出siz[v][u],进而计算出f[u->v]。
接下来考虑如何计算答案。因为期望的线性性,路径长度的期望等于每条边长度期望的和,所以考虑每条边在多少条路径中出现过,即可得出一条有向边u->v对答案的贡献是siz[v][u]*siz[u][v]*f[u->v]/n/n。对每条边各计算一次即可,时间复杂度O(n)。

const p=998244353; var tot,i,u,v:longint; n:int64; head,ret,next,size,w,ans,f,g:array[0..200022] of int64; procedure ins(u,v:longint); begin tot:=tot+1; ret[tot]:=v; next[tot]:=head[u]; head[u]:=tot; end; function gpow(x,k:int64):int64; var t:int64; begin t:=1; while k>0 do begin if k and 1=1 then t:=t*x mod p; x:=x*x mod p; k:=k >> 1; end; exit(t); end; procedure dfs(u,pre:longint); var i,v:longint; begin i:=head[u]; size[u]:=1; while i<>0 do begin v:=ret[i]; if v<>pre then begin dfs(v,u); w[i xor 1]:=2*size[v]-1; w[i]:=2*n-2-w[i xor 1]; size[u]:=size[u]+size[v]; end; i:=next[i]; end; i:=head[u]; while i<>0 do begin v:=ret[i]; if v<>pre then begin f[u]:=(f[u]+f[v]+(size[v])*w[i xor 1]) mod p; g[u]:=(g[u]+g[v]+(size[v])*w[i]) mod p; ans[u]:=(ans[u]+g[v]*(size[u]-size[v]) mod p+(size[v])*(size[u]-size[v])*w[i] mod p+(size[v])*(size[u]-size[v])*w[i xor 1] mod p+f[v]*(size[u]-size[v]) mod p+ans[v]) mod p; end; i:=next[i]; end; end; begin tot:=1; readln(n); for i:=1 to n-1 do begin readln(u,v); ins(u,v); ins(v,u); end; dfs(1,-1); write(gpow(n*n mod p,p-2)*ans[1] mod p); end.

    推荐阅读