【luogu P3412】仓鼠找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+31/4+51/8…=3。有2种情况。

如果a和b都在不同的叶子,E3=E2+1。有2种情况。

如果a=b,E4=0,有3种情况。

所以期望是16/9,有理数取模后就是输出。

思路

首先dp出每个点到其父亲的期望游走距离和每个点父亲到这个点的期望游走距离(可以自己推式子,可以直接拉经典结论),这样我们就相当于把一个双向边拆成两条单向边。然后考虑如何统计答案。
主要到s到t的期望游走距离为s到lca的期望游走距离+lca到t的期望游走距离,所以枚举每个点作为lca,然后利用树形dp+乘法原理统计答案。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100010;
const int mo = 998244353;
vector<int> G[N];
int power(int p,int q)
{
    if (q == 0) return 1; if (q == 1) return p;
    int res = 1; int tmp = p;
    for (; q; q >>= 1, tmp = 1ll * tmp * tmp % mo) if (q & 1)res = 1ll * res * tmp %mo;
    return res;
}
int n,sz[N],a[N],fa[N],num;
ll ans;
void dfs(int u,int pre)
{
    sz[u] = 1;fa[u] = pre;
    for (int i = 0; i < G[u].size(); ++i) if (G[u][i] != pre) dfs(G[u][i],u),sz[u] += sz[G[u][i]];
}
int Mo(int &x){while (x >= mo) x -= mo; while (x < 0) x += mo;}
ll Mo1(ll &x){while (x >= mo) x -= mo; while (x < 0) x += mo;}
int main()
{
    n = read();
    for (int i = 1; i < n; ++i)
    {
        int u = read(), v = read();
        G[u].push_back(v), G[v].push_back(u);
    }
    dfs(1,0);
    for (int i = 1; i <= n; ++i)
    {
        int tot = n - 1;
        for (int j = 0; j < G[i].size(); ++j) 
        {
            if (G[i][j] == fa[i]) continue;
            ll pre = 0;int s = sz[G[i][j]];
            pre += (2ll * (n - s ) * (n - s ) - (n - s )) * s %mo; Mo1(pre);
            ans += pre, Mo1(ans);
            tot -= sz[G[i][j]]; 
        }
        if (tot)
        {
            ll pre = 0;int s = tot;
            pre += (2ll * (n - s ) * (n - s ) - (n - s )) %mo * s; Mo1(pre);
            ans += pre, Mo1(ans);       
        }
    }
    int Tot = 1ll * n * n % mo;
    ans = 1ll * ans * power(Tot,mo - 2) %mo;
    printf("%lld\n",ans);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值