题目
小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;
}