题意
小Q有一个集合 S ,它的元素个数 |S|=n 。
对于 S 的任意一个子集合 T ,定义
f(T)=|T|k
,定义 T 关于 S 的补集为 S−T 。
小Q想知道,如果他等概率地选择一个 S 的子集 T ,那么 f(T)−f(S−T) 的方差是多少。
由于这个方差值可能很大,不妨设其为 v ,你只需要给出
(v⋅2n)%m
的值即可。
k ≤ n ≤ 10^(10^6) , 1 ≤ k ≤ 10^6,其中m是质数,满足2 ≤ m ≤ 10^6。
题解
方差的定义:
E[(x−E(x))2]
,根据期望线性性质可化简为
E(x2)−E(x)2
注意到E(x)恒为0,所以我们只需要计算
(E(x2)∗2n)%m
即可。
E(x2)∗2n=∑ni=0(ni)∗(ik−(n−i)k)2
注意到n很大,肯定不能直接求。
注意到右边的
(ik−(n−i)k)2
模m是循环的。但是前面还有一个组合数。
这里要用到lucas定理:
(nm)=(n%Pm%P)∗(⌊n/P⌋⌊m/P⌋)(modP)
这个定理比较适合模数小的问题。
对于i mod m相同的每一组,
(n%Pi%P)
都是相同的。
按这个思路,可得
E(x2)∗2n
=∑⌊n/m⌋i=0∑n%mj=0(ni∗m+j)∗((i∗m+j)k−(n−(i∗m+j))k)2
=∑⌊n/m⌋i=0∑n%mj=0(⌊n/m⌋i)∗(n%mj)∗((i∗m+j)k−(n−(i∗m+j))k)2
=∑⌊n/m⌋i=0(⌊n/m⌋i)∑n%mj=0(n%mj)∗(jk−(n−j)k)2
内层那个sigma就和i无关了,每次都是一样的,所以
=2⌊n/m⌋∑n%mj=0(n%mj)∗(jk−(n−j)k)2
直接求即可。
这题怎么卡常啊,半天才过……
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
int Nm,N_m,K,m,inv[1000005],powK[1000005],fac[1000015],ans; //N_m == n%m Nm == n/m
char st[1000005];
inline LL power(int a,int b){
LL res=1;
for(LL w=a;b;w=w*w%m,b>>=1) if(b&1) res=(res*w)%m;
return res;
}
inline LL C(int a,int b){
return ((LL)fac[a])*inv[((LL)fac[b])*fac[a-b]%m]%m;
}
int main(){
freopen("51nod25D.in","r",stdin);
freopen("51nod25D.out","w",stdout);
scanf("%s%d%d",st,&K,&m); K%=m-1;
fac[0]=1; for(int i=1;i<=1000005;i++) fac[i]=((LL)fac[i-1])*i%m;
inv[1]=1; for(int i=2;i<=m-1;i++) inv[i]=(LL)(m-m/i)*inv[m%i]%m;
int len=strlen(st),t=0;
for(int i=0;i<=len-1;i++){
N_m=((N_m<<3)+(N_m<<1)+st[i]-'0')%m;
t=(t<<3)+(t<<1)+st[i]-'0'; Nm=((Nm<<3)+(Nm<<1)+t/m)%(m-1); t%=m;
}
for(int i=0;i<=N_m;i++) powK[i]=power(i,K);
for(int j=0;j<=N_m;j++){
int num=powK[j]-powK[N_m-j];
ans+=(C(N_m,j)*num%m)*num%m; ans%=m;
}
ans=(ans*power(2,Nm))%m;
printf("%d\n",(ans+m)%m);
return 0;
}