传送门:https://ac.nowcoder.com/acm/contest/11190/B
题意
思路
$排列组合题,基本都是考虑“数”的贡献。$
$所以我们针对每个数字1,2…n出现的次数,即贡献为num_i*\frac{1}{i!},num_i表示i出现的次数。$
$即ans=\sum_{i=1}^{n}num_i*\frac{1}{i!}$
$比如先考虑1,先拿出一个1,然后还剩下n-1,则我们让他们随意组合。$ $假设n-1组成4个数字,则由捆绑法可得:$
$n-1的组成4个数字的种数为C_{n-2}^{3},然后我们的1可以插入5个位置,则*5.$
$我们可以让n-1组成1到n-1个数字,进而得出,1出现的总次数为:$
$C_{n-2}^{0}*2+C_{n-2}^{1}*3+…+C_{n-2}^{n-3}*(n-1)+C_{n-2}^{n-2}*n$
$由组合恒等式算出来,这个式子为2^{n-1}+(n-2)*2^{n-3}$
$则1的总贡献为\frac{1}{1!}*(2^{n-1}+(n-2)*2^{n-3})$
$然后计算2,3一直到n的贡献,加起来即可。注意一些边界问题,越界了都当成0.$
$分母就是n个1能有多少种组成方法:$
- $组成1个数,C_{n-1}^{0}$
- $组成2个数,C_{n-1}^{1}$
- …
- $组成n个数字,C_{n-1}^{n-1}$
$由二项式定理得,分母为2^{n-1}$
$总结一下ans:$
$$\frac{\sum_{i=1}^{n}2^{n-i}+(n-i-1)*2^{n-i-2}}{2^{n-1}}$$
$预处理然后O(n)计算。$
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| ##include "bits/stdc++.h" using namespace std;
typedef long long ll;
const ll mod = 998244353; const int N = 5e5 + 10;
ll fac[N], inv[N], invF[N]; ll bit[N], invbit[N];
ll quick_pow(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans % mod; }
void init() { ll inv2 = quick_pow(2, mod - 2); invbit[0] = bit[0] = fac[0] = fac[1] = inv[0] = inv[1] = invF[0] = invF[1] = 1; bit[1] = 2; invbit[1] = inv2; for(int i = 2;i < N; i++) { fac[i] = fac[i - 1] * i % mod; inv[i] = (mod - mod / i) * inv[mod % i] % mod; invF[i] = invF[i - 1] * inv[i] % mod; bit[i] = bit[i - 1] * 2 % mod; invbit[i] = invbit[i - 1] * inv2 % mod; } }
void solve() { init(); int _; cin >> _; while(_--) { int n; cin >> n; ll ans = 0; for(int i = 1;i <= n; i++) { ans = (ans + invF[i] * (bit[n - i] + (n - i - 1 >= 0 ? n - i - 1 : 0) * bit[(n - i - 2 >= 0 ? n - i - 2 : 0)] % mod) % mod) % mod; } ans = ans * invbit[n - 1] % mod; cout << (ans % mod + mod) % mod << endl; } }
signed main() { solve(); }
|
本文作者:jujimeizuo
本文地址: https://blog.jujimeizuo.cn/2021/05/18/nowcoder50-b-random-eat-cake/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!