HDU6267逆元+思维

题意:

​ Hakase provides Nano with a problem. There is a rooted tree with values on nodes. For each query, you are asked to calculate the sum of the values in the subtree. However, Nano is a rookie so she decides to guess the answer. She has known how the data generator works: it identifies the nodes with labels from 0 to n − 1 and then visits them one by one. For each i (1 ≤ i ≤ n), the generator selects a node whose label is smaller than i to be its father. The pseudocode is like this:

  for i = 1 to n - 1:
      father[i] = random(0, i - 1);

​ where random(a, b) randomly generates a uniformly distributed random integer in range [a, b].

​ Knowing n and the value of the i-th node ai, Nano decides to randomly choose a subtree and sum up all of the values in the subtree as the answer. Now Hakase wants to know what the expectation of the answer is. Can you help her?

Input

​ The first line contains an integer T (1 ≤ T ≤ 10) representing the number of test cases.
For each test case, the first line contains an integer n (1 ≤ n ≤ 100000), the number of the nodes in the rooted tree.
​ The second line contains n integers a0, a1, …, an−1 (1 ≤ ai ≤ 100000) represent the values of nodes.

Output

​ It can be proven that the answer equals to an irreducible fraction p/q. For each test case, print p ∗ q−1 mod 998244353 in one line. q−1 is the inverse of q under module number 998244353.

Example

在这里插入图片描述

Explanation

​ The shape of the tree in the first test case is unique. The father of node 1 is 0. It is possible to choose node 0 or 1 with equal possibility. The sum of the subtree with 0 as the root is 2 while the sum of the subtree with 1 as the root is 1. So the expectation is (2 + 1)/2 = 3/2. The output is 3 ∗ 2−1 mod 998244353 = 499122178.

​ There are two possible shapes in the second test case, node 1’s father destines to be 0, but node 2’s father might be node 0 or node 1. Both conditions are equally possible.

​ If node 2’s father is node 0, we randomly choose a node. The sum of the subtree with node 0 as the root is 6. The sum of the subtree with node 1 as the root is 2. The sum of the subtree with node 2 as the root is 3.

​ If node 2’s father is node 1, we randomly choose a node. The sum of the subtree with node 0 as the root is 6. The sum of the subtree with node 1 as the root is 5. The sum of the subtree with node 2 as the root is 3.

​ So the expectation is (6+2+3+6+5+3)/6 = 25/6. The output is 25∗6−1 mod 998244353 = 166374063.

​ 这么一大串英文,翻译成汉语大概也就是说让你输入一个T,代表有T组样例。每组样例第一行输入一个n,代表一个树有n个节点,然后循环输入每个节点的权值。它组成树的方式是:k号节点等概率的选择0~k-1中任意一个节点当它的父节点。然后随意选一个节点,问以它为根节点组成的子树的权值和的数学期望是多少?

思路:

​ n个节点,每个节点等概率的选择0~k-1中任意一个节点当它的父节点。那么这n个节点组成树的可能性为(n - 1)!个。例如4个节点可能组成以下6种树:

在这里插入图片描述

​ 又因为任选一个节点当根节点,所以共有n * (n - 1),也就是n!种子树

​ 因此我们可以通过求(每个节点在全部子树的贡献和)/ n!来求数学期望值。

​ 我们可以看到,0号节点在全部子树的贡献是6次(每种树只有选取0号节点当根节点才包含0号节点),也就是3!

​ 1号节点在全部子树的贡献是12次(每种树只有选取0号节点或者1号节点才包含1号节点),也就是3! + 3! / 1

​ 2号节点在全部子树的贡献是15次(有3种树选0,1,2号节点当根节点包含2号节点,有3种树选0,2号节点当根节点才包含2号节点),也就是3! + 3! / 1 + 3! / 2

​ 3号节点在全部子树的贡献是17次(有1种树选0,1,2,3号节点当根节点包含3号节点,有2种树选0,1,3号节点当根节点包含3号节点,有1种树选0,2,3号节点当根节点包含3号节点,有2种树选0,3号节点当根节点包含3号节点),也就是3! + 3! / 1 + 3! / 2 + 3! / 3

​ 我们发现第k号节点的贡献为:(n - 1)! + (n - 1) ! / 1 + … + (n - 1) / k

​ 所以我们设i号节点的贡献为pi,权值为qi,那么每个节点在全部子树的贡献和为∑pi*qi (i从0到n-1),再除以n!即为数学期望。因为需要取余998244353且包含除法,因此需要用到求逆元。

代码:

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#define ll int64_t
#define d int32_t
#define f double
#define r return
#define N 100000
#define mod 998244353
#define mem(a) memset(a, 0, sizeof(a))
#define scanfd(a) scanf("%d", &a)
#define scanfl(a) scanf("%lld", &a)
#define printfl(a) printf("%lld\n", a)
#define For(i, star, endd) for (d i = star; i <= endd; i++)
#define Forr(i, endd, star) for (d i = endd; i >= star; i--)

ll q[N + 5];        //存储每个节点的权值
ll fact[N + 5];       //存储阶乘
ll inv[N + 5];      //存储阶乘的逆元
ll inv1[N + 5];     //存储线性逆元
ll num[N + 5];      //存储节点的贡献值

//扩展欧几里得算法
ll extend_gcd (ll a, ll b, ll &x, ll &y) {
    if (a == 0 && b == 0) r -1;
    if (b == 0) {
        x = 1;
        y = 0;
        r a;
    }
    ll t = extend_gcd(b, a % b, y, x);
    y -= a / b * x;
    r t;
}

//求a关于mod的逆元
ll mod_reverse (ll a) {
    ll x, y;
    ll t = extend_gcd(a, mod, x, y);
    if (t == 1) r (x % mod + mod) % mod;
    r -1;
}

//预处理1~100000阶乘及逆元,处理线性逆元
void init() {
    fact[0] = fact[1] = 1;
    inv1[0] = inv1[1] = 1;
    For (i, 2, N) {
        fact[i] = fact[i - 1] * i % mod;
        inv1[i] = (mod - mod / i) * inv1[mod % i] % mod;
    }
    inv[N] = mod_reverse(fact[N]);
    Forr (i, N - 1, 0) {
        inv[i] = inv[i + 1] * (i + 1) % mod;
    }
    r;
}

//处理权值和
ll work(d n) {
    ll ans = 0;
    num[0] = fact[n - 1];
    ans = (ans + num[0] * q[0]) % mod;
    For(i, 1, n - 1) {
        num[i] = (num[i - 1] + fact[n - 1] * inv1[i]) % mod;
        ans = (ans + num[i] * q[i]) % mod;
    }
    r ans;
}

d main () {
    init();
    d T, n;
    scanfd(T);
    while (T--) {
        scanfd(n);
        For (i, 0, n - 1) {
            scanfl(q[i]);
            q[i] %= mod;
        }
        ll ans = work(n) * inv[n] % mod;
        printfl(ans);
    }
    r 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值