1778 小Q的集合
原题连接
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1778
题目绕了一点。并没有直接给出数字形式。
而且这个题有好像有很多方法。
我也YY了一个(不过还是题解多效率高)。
题目中是给定了一个集合
S
并且有 ∣∣S∣∣=n
要说明的是。这个集合是没有重复元素的集合。(这一点很重要)
其实不特别说明。一般集合也都没有重复元素。
集合S的子集数量等价于 从 S 中取元素的取法数量。(高中数学)
记 子集数量为
x
x=∑i=0n(ni)=1i∗1n−i(ni)=(1+1)n=2n
那么我们对所有子集进行编号。 记第i个子集为
Ti
因为集合没有重复元素。 那么有:
任意
Ti
都一一对应 一个 补。即:
S−Ti
也就是有:
∑i=1x∣∣Ti∣∣=∑i=1x∣∣S−Ti∣∣
更一般的:
∑i=1xf(Ti)=∑i=1xf(S−Ti)
那么。令原题中的期望为
E
:
∑i=1x∣∣Ti∣∣=∑i=1x∣∣S−Ti∣∣
∑i=1xf(Ti)=∑i=1xf(S−Ti)
xE=2nE=∑i=1x[ f(Ti)−f(S−Ti) ]=∑i=1xf(Ti)−∑i=1xf(S−Ti)=∑i=1xf(Ti)−∑i=1xf(Ti)=0
所以。方差
v
也就有:
answer=2nv=xv=xσ2=∑i=1x[ f(Ti)−f(S−Ti)−E]2=∑i=1x[ f(Ti)−f(S−Ti)]2
我们更加注意集合元素的数量。而对于包含
i
个元素的集合数量是:
(ni)
所以:
answer=∑i=0n[ik−(n−i)k]2(ni)% m
对于这个和式是否有封闭形式。 我不知道。不过有就是结论提了。哈哈.
令 :
G(i)=[ik−(n−i)k]2(n%mi%m)
在
mod m
的意义下(卢卡斯定理)
answer=∑i=0nG(i)(⌊nm⌋⌊im⌋)
观察
G(i)
有:
G(i+m)=[(i+m)k−(n−(i+m))k]2=G(i) (mod m)
记 :
S=∑i=0nG(i)
记:
S(l,r)=∑i=lrG(i) i>m或者i<0时G(i)=0
则有:
answer=∑i=0nG(i)(⌊nm⌋⌊im⌋)=∑i=0⌊nm⌋∑b=0m−1[G(im+b)(⌊nm⌋i)]−S(n%m+1 , n+1)
其中
∑b=0m−1[G(im+b)(⌊nm⌋i)]=(⌊nm⌋i)∑b=0m−1G(im+b)=(⌊nm⌋i)S
漂亮 。这样就有:
∑i=0⌊nm⌋∑b=0m−1[G(im+b)(⌊nm⌋i)=S∑i=0⌊nm⌋(⌊nm⌋i)=S∗2⌊nm⌋
哈哈哈哈:
answer=S∗2⌊nm⌋−S(n%m+1 ,n+1)
这是一个很简洁的答案。 但是由于快速幂 比较耗时间。 要细心些哦。
当然。我们必须要结合费马小定理来进一步优化:
a(p−1)=1 (mod p)
因为m为素数,所以:
2⌊nm⌋=2(⌊nm⌋ %(m−1)) (mod m)
answer=∑i=0n[ik−(n−i)k]2(ni)% m
G(i)=[ik−(n−i)k]2(n%mi%m)
answer=∑i=0nG(i)(⌊nm⌋⌊im⌋)
G(i+m)=[(i+m)k−(n−(i+m))k]2=G(i) (mod m)
S=∑i=0nG(i)
S(l,r)=∑i=lrG(i) i>m或者i<0时G(i)=0
answer=∑i=0nG(i)(⌊nm⌋⌊im⌋)=∑i=0⌊nm⌋∑b=0m−1[G(im+b)(⌊nm⌋i)]−S(n%m+1 , n+1)
∑b=0m−1[G(im+b)(⌊nm⌋i)]=(⌊nm⌋i)∑b=0m−1G(im+b)=(⌊nm⌋i)S
∑i=0⌊nm⌋∑b=0m−1[G(im+b)(⌊nm⌋i)=S∑i=0⌊nm⌋(⌊nm⌋i)=S∗2⌊nm⌋
answer=S∗2⌊nm⌋−S(n%m+1 ,n+1)
a(p−1)=1 (mod p)
2⌊nm⌋=2(⌊nm⌋ %(m−1)) (mod m)
下面是代码:
#include <stdio.h>
#define MAXN 1000005
typedef long long LL;
LL n,m,k,r;
LL pow(LL,int);
LL clat_divie();// 计算n/m 返回余数,商 保存在N[]
void init();//预处理逆元
LL G[MAXN];
int S[MAXN];
LL C[MAXN];
LL Ivn[MAXN];
LL Po[MAXN];
char s[MAXN];
int N[MAXN],size;
int main ()
{
//freopen("/Users/gaoxusheng/Desktop/51nod_Problem_1778_Test_25_In.txt","rw",stdin);
scanf("%s",s);
scanf("%lld %lld",&k,&m);
init();
r=n=clat_divie();
C[0]=1;
for(int i=0;i<m;i++)C[i+1]=C[i]*(r-i)*Ivn[i+1]%m;
for(int i=0,sz=(int)m+1;i<sz;i++)
{
LL &tmp=Po[i];
tmp=1;
LL u=i;
int b=(int)k;
while(b)
{
if(b&1)
tmp=tmp*u%m;
u=u*u%m;
b>>=1;
}
}
for(int i=0;i<=m;i++)
{
G[i]=Po[i]*(Po[i]-Po[n--]+m);
if(n<0)n+=m;
G[i]*=C[i];
G[i]%=m;
}
int M=(int)m-1;
for(int i=(int)m;i>-1;i--)
{
S[i]=(S[i+1]+(int)G[i]);
if(S[i]>M)S[i]-=m;
}
LL tmp=0;
m--;
for(int i=0;i<size;i++)
{
tmp=tmp*10+N[i];
if(tmp>=m) tmp%=m;
}
m++;
LL ans=(LL)S[0]*pow(2,(int)tmp)%m;
ans-=(LL)S[r+1];
if(ans<0)ans+=m;
printf("%lld\n",(ans<<1)%m);
return 0;
}
void init()
{
Ivn[1]=1;
for(int i=2;i<m;i++)
Ivn[i]=m-Ivn[m%i]*(m/i)%m;
}
LL pow(LL a,int b)
{
LL tmp=1;
while(b)
{
if(b&1)
tmp=tmp*a%m;
b>>=1;
a=a*a%m;
}
return tmp;
}
LL clat_divie()
{
LL tmp=0;
for(int i=0;s[i];i++)
{
tmp=tmp*10+s[i]-'0';
if(tmp>=m)
{
N[size]=(int)tmp/m;
tmp%=m;
}
size++;
}
bool flag=true;
for(int i=0;i<size;i++)
if(N[i]>0)
{
flag=false;
int t;
for(t=0;i<size;i++,t++) N[t]=N[i];
size=t;
break;
}
if(flag)size=0;
return tmp;
}