51Nod1778 小Q的集合 【组合数】【Lucas定理】

题目分析:

题解好高深......

我给一个辣鸡做法算了,题解真的看不懂。

注意到方差恒为$0$,那么其实就是要我们求$\sum_{i=0}^{n}\binom{n}{i}(i^k-(n-i)^k)^2$。

转换一下

    $\sum_{i=0}^{n}\binom{n}{i}(i^k-(n-i)^k)^2$

$=2\sum_{i=0}^{n}\binom{n}{i}(i^{2k}-i^k(n-i)^k)$

注意到$i^{2k}$与$i^k(n-i)^k$在模$m$意义下都是一个周期为$m$的数列,那么我们需要求出每隔$m$个的组合数的和,即:

$2\sum_{i=0}^{m-1}(\sum_{j=0}^{\frac{n-i}{m}}\binom{n}{i+j*m}(i^{2k}-i^k(n-i)^k))$

把焦点放到内部的求和里面去,它是很简单的一个式子,试着转化它。首先我们可以根据Lucas定理分析出,对于外部的$i$,它的结果中一定有$ \binom{n\%m}{i} $

剩下的是什么?首先有不等式$i+j*m \leq n\%m + \left \lfloor \frac{n}{m} \right \rfloor*m$。在这里我们毫无疑问地认为$i \leq n\%m$。否则对结果无影响。

我们接受$\left \lfloor \frac{n}{m} \right \rfloor$的所有影响,取满它,取满一排,它是$2$的次幂。

所以这个式子就等于$2\sum_{i=0}^{m-1}(\binom{n\%m}{i}*2^{\left \lfloor \frac{n}{m} \right \rfloor}(i^{2k}-i^k(n-i)^k))$

$\left \lfloor \frac{n}{m} \right \rfloor$很大,采用费马定理优化。

这样我们就可以解决它在$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 }

 

posted @ 2018-06-12 19:47  menhera  阅读(400)  评论(0编辑  收藏  举报