• 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*m$。在这里我们毫无疑问地认为$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 }
  • 相关阅读:
    判断平面的一堆点是否在两条直线上
    约数的个数 + 贪心
    划分树板子
    如何获取前端提交来得json格式数据
    post 和php://input 转
    使用Guzzle执行HTTP请求
    redis集群搭建 不用ruby
    systemctl命令
    canal 配置 多个监听 推送到不同mq
    canal 整合RabbitMQ
  • 原文地址:https://www.cnblogs.com/Menhera/p/9174249.html
Copyright © 2020-2023  润新知