题目
小Q有一个集合 ,它的元素个数
。
对于 的任意一个子集合
,定义
,定义
关于
的补集为
。
小Q想知道,如果他等概率地选择一个 的子集
,那么
的方差是多少。
由于这个方差值可能很大,不妨设其为 ,你只需要给出
的值即可。
题解
可以看出具有对称性,可以知道
方差,
但注意到很大则需要化解,
大小不确定,则需要
定理
我们类似于算法,对
关于
做带余除法(
则可以使用)
(这里用到了
定理)
外层用二项式定理化为的幂,内层预处理逆元计算即可
#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
using LL = long long;
const int MAXN = 1e6 + 5;
LL N_MOD, NM, K, MOD;
char s[MAXN];
LL fac[MAXN], inv_fac[MAXN];
void pre_getfac(int);
LL qpow(LL, int);
LL CC(int, int);
LL ans;
int main(){
int i;
scanf("%s%lld%lld", s + 1, &K, &MOD);
int len = strlen(s + 1);
for(i = 1; i <= len; i++)
N_MOD = 10 * N_MOD + s[i] - '0', N_MOD %= MOD;
pre_getfac(min(N_MOD, MOD - 1));
K %= (MOD - 1);
int top = N_MOD / 2;
for(i = 0; i <= top; i++){
LL tmp = (qpow(N_MOD - i, K) - qpow(i, K) + MOD) % MOD;
tmp = tmp * tmp % MOD;
ans = (ans + CC(N_MOD, i) * tmp % MOD) % MOD;
}
ans = ans * 2 % MOD;
LL tmp = 0;
for(i = 1; i <= len; i++){
tmp = 10 * tmp + s[i] - '0';
NM = 10 * NM + tmp / MOD;
tmp %= MOD, NM %= (MOD - 1);
}
ans = ans * qpow(2, NM % (MOD - 1)) % MOD;
printf("%lld\n", ans);
return 0;
}
LL CC(int n, int m){
if(m > n) return 0;
if(n < MOD) return fac[n] * inv_fac[n - m] % MOD * inv_fac[m] % MOD;
return CC(n / MOD, m / MOD) * CC(n % MOD, m % MOD) % MOD;
}
void pre_getfac(int top){
int i;
fac[0] = 1;
for(i = 1; i <= top; i++) fac[i] = fac[i - 1] * i % MOD;
inv_fac[top] = qpow(fac[top], MOD - 2);
for(i = top - 1; i >= 0; i--)
inv_fac[i] = inv_fac[i + 1] * (i + 1) % MOD;
}
LL qpow(LL x, int n){
LL res = 1;
while(n){
if(n & 1) res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}

本文解析了一道算法竞赛题目,探讨如何计算集合所有子集方差的期望值,通过数学推导和编程实现,利用二项式定理、逆元计算等技巧,解决了方差值可能过大的问题。
643

被折叠的 条评论
为什么被折叠?



