51Nod1778 小Q的集合 组合数Lucas定理
Posted menhera
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51Nod1778 小Q的集合 组合数Lucas定理相关的知识,希望对你有一定的参考价值。
题目分析:
题解好高深......
我给一个辣鸡做法算了,题解真的看不懂。
注意到方差恒为$0$,那么其实就是要我们求$sum_{i=0}^{n}inom{n}{i}(i^k-(n-i)^k)^2$。
转换一下
$sum_{i=0}^{n}inom{n}{i}(i^k-(n-i)^k)^2$
$=2sum_{i=0}^{n}inom{n}{i}(i^{2k}-i^k(n-i)^k)$
注意到$i^{2k}$与$i^k(n-i)^k$在模$m$意义下都是一个周期为$m$的数列,那么我们需要求出每隔$m$个的组合数的和,即:
$2sum_{i=0}^{m-1}(sum_{j=0}^{frac{n-i}{m}}inom{n}{i+j*m}(i^{2k}-i^k(n-i)^k))$
把焦点放到内部的求和里面去,它是很简单的一个式子,试着转化它。首先我们可以根据Lucas定理分析出,对于外部的$i$,它的结果中一定有$ inom{n\%m}{i} $。
剩下的是什么?首先有不等式$i+j*m leq n\%m + left lfloor frac{n}{m} ight floor$。在这里我们毫无疑问地认为$i leq n\%m$。否则对结果无影响。
我们接受$left lfloor frac{n}{m} ight floor$的所有影响,取满它,取满一排,它是$2$的次幂。
所以这个式子就等于$2sum_{i=0}^{m-1}(inom{n\%m}{i}*2^{left lfloor frac{n}{m} ight floor}(i^{2k}-i^k(n-i)^k))$
$left lfloor frac{n}{m} ight floor$很大,采用费马定理优化。
这样我们就可以解决它在$O(mlogk)$的时间内了。
注意,如果我们会线性求逆元以及线性筛,可以去掉log。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 1048576; 5 6 int mod,nr,k,np; 7 char str[maxn]; 8 int str2[maxn]; 9 10 int c[maxn],rw[maxn]; 11 12 void read(){ 13 scanf("%s",str); 14 scanf("%d%d",&k,&mod); 15 int len = strlen(str); 16 for(register int i=0;i<len;i++){ nr = nr*10+str[i]-‘0‘; nr %= mod; } 17 for(register int i=0,num=0;i<len;i++){ 18 np = np*10+str[i]-‘0‘; str2[i] = np/mod; np %= mod; 19 } np = 0; 20 for(register int i=0;i<len;i++){ 21 np = np*10+str2[i]; np %= (mod-1); 22 } 23 } 24 25 int fast_pow(int now,int pw){ 26 int ans = 1,dd = now,base = 1; 27 while(base <= pw){ 28 if(base & pw){ans = (1ll*ans*dd)%mod;} 29 dd = (1ll*dd*dd)%mod; 30 base<<=1; 31 } 32 return ans; 33 } 34 35 int prime[maxn/8],flag[maxn],num; 36 void get_prime(int N){ 37 rw[1] = 1; flag[1] = 1; 38 for(int i=2;i<=N;i++){ 39 if(!flag[i]){prime[++num]=i;rw[i]=fast_pow(i,k);} 40 for(int j=1;j<=num&&i*prime[j]<=N;j++){ 41 flag[i*prime[j]] = 1; 42 rw[i*prime[j]] = (1ll*rw[i]*rw[prime[j]])%mod; 43 if(i%prime[j] == 0) break; 44 } 45 } 46 } 47 48 void init(){ 49 get_prime(nr); 50 np = fast_pow(2,np+1); c[0] = 1; 51 for(register int i=1;i<=nr;i++){ 52 c[i] = (1ll*c[i-1]*(nr-i+1))%mod; 53 c[i] = (1ll*c[i]*fast_pow(i,mod-2))%mod; 54 } 55 } 56 57 void work(){ 58 int ans = 0; 59 for(register int i=0;i<mod;i++){ 60 if(i > nr) break; 61 int hh = rw[i]; 62 int pp = ((1ll*hh*hh)%mod) - (1ll*hh*rw[nr-i])%mod; 63 if(pp < 0) pp += mod; 64 pp = (1ll*pp*c[i])%mod; 65 ans += pp; if(ans >= mod) ans -= mod; 66 } 67 ans += mod; if(ans >= mod) ans -=mod; 68 ans = (1ll*np*ans)%mod; 69 printf("%d",ans); 70 } 71 72 int main(){ 73 read(); 74 init(); 75 work(); 76 return 0; 77 }
以上是关于51Nod1778 小Q的集合 组合数Lucas定理的主要内容,如果未能解决你的问题,请参考以下文章