[51nod]1778 小Q的集合[lucas定理+组合计数]

题目

小Q有一个集合S ,它的元素个数|S|=N 。

对于 T 的任意一个子集合 T,定义 f(T)=|T|^k ,定义T 关于S 的补集为 S - T

小Q想知道,如果他等概率地选择一个 S 的子集T ,那么 f(T)-f(S-T)的方差是多少。

由于这个方差值可能很大,不妨设其为 v ,你只需要给出 (v2^N)\%mod 的值即可。


题解

可以看出f(T)-f(S-T)具有对称性,可以知道E(x)=0

方差E[(x-E(x))^2]=\sum\binom{N}{i} (i^k-(N-i)^k)^2/2^N,ans=v2^N=\sum\binom{N}{i} (i^k-(N-i)^k)^2

但注意到N很大则需要化解,N,MOD大小不确定,则需要lucas定理

我们类似于bsgs算法,对N关于MOD做带余除法(lucas则可以使用)

\\ ans\\ =\sum\binom{N}{i} (i^k-(N-i)^k)^2\\ =\sum_{i=0}^{[\frac{N}{MOD}]} \sum_{j=0}^{N\%MOD} \binom{N}{i*MOD+j} ((i*MOD+j)^k-(N-(i*MOD+j))^k)^2\\ \equiv \sum_{i=0}^{[\frac{N}{MOD}]} \binom{[\frac{N}{MOD}]}{i} \sum_{j=0}^{N\%MOD} \binom{N\%MOD}{j} (j^k-(N\%MOD-j)^k)^2 \pmod {MOD}(这里用到了lucas定理)

外层用二项式定理化为2的幂,内层预处理逆元计算即可

#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;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值