zoukankan      html  css  js  c++  java
  • BZOJ2627 JZPKIL

                一句话题意:求$sum_{i=1}^ngcd(i,n)^xlcm(i,n)^y$,多组数据,模$1e9+7$。

                30%的数据,$x=y$
                另30%的数据,$n<=10^9$, $x, y<=100$
           100%的数据,$T<=100, 1<=n<=10^{18}, 0<=x, y<=3000$

           orz出题人顾宇宙!!!orz全网题解的源头ydc!!!感觉ydc的题解已经写得很清楚了,但是为了内部交流还是写一写……下面的公式中$[x]$表示$x==1$。

    推导部分

    Part1 原式整理$$sum_{i=1}^ngcd(i,n)^xlcm(i,n)^y$$$$=sum_{i=1}^n(in)^ygcd(i,n)^{x-y}$$$$=n^ysum_{i=1}^ni^ygcd(i,n)^{x-y}$$$$=n^ysum_{d|n}d^{x-y}sum_{k=1,[gcd(k,frac{n}{d}))]}^{frac{n}{d}}(kd)^y$$$$=n^ysum_{d|n}d^xsum_{k=1,[gcd(k,frac{n}{d})]}^{frac{n}{d}}k^y$$

    Part2 套路反演

              这一部分我们来整理上式最后的$sum_{k=1,[gcd(k,frac{n}{d})]}^{frac{n}{d}}k^y$

    $$sum_{i=1,[gcd(i,n)]}^ni^y$$$$=sum_{i=1}^ni^ysum_{d|gcd(i,n)}mu(d)$$$$=sum_{d=1}^nmu(d)sum_{i=1}^{frac{n}{d}}(id)^y$$$$=sum_{d=1}^nmu(d)d^ysum_{i=1}^{frac{n}{d}}i^y$$

    Part3 伯努利数

           可以发现Part2的最后我们把Part1的幂之和化为了从1开始的连续一段……这有什么用呢?我们可以套结论了!有一个叫伯努利数的东西(下文推导中用$B_k$表示第$k$个伯努利数),在网上搜一搜的话资料不少,百度百科里基本的公式也给了一些。刚开始看证明说什么从生成函数定义的角度可以证明它和自然数幂和的数学关系,但是我后来看到它就是在求自然数幂和的过程中被发明的就感觉证这个没必要了……求伯努利数的方法放在实现部分里,现在我们继续关注推导过程。

    $$sum_{i=1}^ni^m=frac{1}{m+1}sum_{k=0}^mC_{m+1,k}B_kn^{m-k+1}$$

    大多数情况下伯努利数都是这么用的……可以看成公式

    你可能会发现伯努利数的公式很乱,有人用$n$的幂有人用$n+1$的幂还有人带-1的幂……然而因为不会证我也不能确切地说哪一种是对的。维基百科上大概是说伯努利数有两类,两类伯努利数的区别在于B1。第一类伯努利数,由美国国家标准技术研究所 (NIST)制定,在这标准下B1=-1/2;第二类伯努利数,又被称为是“原始的伯努利数”,在这标准下B1=1/2。$n+1$的式子是关于第一类伯努利数的,而$n$的式子是关于第二类伯努利数的;第一类伯努利数乘上-1的幂即为第二类伯努利数……假装他们都是对的好了编套理论真难啊

    设$t_k=frac{1}{m+1}C_{m+1,k}B_k$,则$sum_{i=1}^ni^m=sum_{k=0}^mt_kn^{m-k+1}$

    Part4 积性函数

           即使化成这步代入原式它也不好处理……不过看一眼$10^{18}$的数据范围,分解质因数来做好像是一种历史必然?so现在我们试一试把积性函数的部分整理到一起会发生什么……

    $$n^ysum_{d|n}d^xsum_{d_1|frac{n}{d}}mu(d_1)d_1^ysum_{k=0}^yt_k(frac{n}{dd_1})^{y-k+1}$$

    $$=sum_{k=0}^yt_kn^ysum_{d|n}d^xsum_{d_1|frac{n}{d}}mu(d_1)d_1^y(frac{n}{dd_1})^{y-k+1}$$

           这时候除去$t_k$的部分就是一个积性函数了,$y$很小只有$3000$,我们可以枚举$k$然后分解质因数求后面的部分。那么就变成了对每一个质因子$p_i$的$k_i$幂$p_k$求$p_k^ysum_{d|p_k}d^xsum_{d_1|frac{p_k}{d}}mu(d_1)d_1^y(frac{p_k}{dd_1})^{y-k+1}$,又因为$mu(d_1)$只有在$d_1=1$时为1、$d_1=p_i$时为-1而其他情况下都为0,式子可以直接变成$p_k^ysum_{d|p_k}d^x((frac{p_k}{d})^{y-k+1}-p_i^yfrac{p_k}{dp_i}^{y-k+1})$,这就是推式子的终点了。

    实现部分

    Part1 伯努利数

             你问我伯努利数怎么求啊……   

    $sum_{k=0}^nC_{n+1,k}B_k=n+1$  

    所以杨辉三角求一下组合数就可以$O(k^2)$求出所有伯努利数了。

    Part2 分解质因数

            给$10^{18}$分解质因数?需要$n^{frac{1}{4}}$的算法!Miller-Rabin和Pollard-Rho可以解决,关于这两个算法我们的朋友Doggu的blog堪称全网最良心。

    Part3 快速乘

             $10^{18}*10^{18}$应该怎么做?快速乘显然是必要的。这是ydc改良过的快速乘板子。

    1 inline LL mul(LL x,LL y,LL z)
    2 {
    3     return (x*y-(LL)(((long double)x*y+0.5)/(long double)z)*z+z)%z;
    4 }

    好像少了点什么东西

            没有错,这题是有部分分的吧?对于$x=y$的$30$分,式子就是$sum_{i=1}^n(in)^y$,如果你会用伯努利数就可以拿到这一部分的分。对于$n<=10^9$的$30$分,伯努利数仍然是必要的,不过分解质因数可以$sqrt{n}$来做。看来出题人只打算给会伯努利数的人分数(显然我不在此之列,所以部分分和正解一样困难……)。想打部分分的同学可以去cogs上提交。

    最后放代码,完结撒花。
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cstdlib>
      5 #include<algorithm>
      6 #define ll long long
      7 using namespace std;
      8 const int mod=1e9+7;
      9 const int sj=3010;
     10 const int ss=110;
     11 inline ll read()
     12 {
     13     ll jg=0;int jk=getchar()-'0';
     14     while(jk<0||jk>9)    jk=getchar()-'0';
     15     while(jk>=0&&jk<=9)  jg*=10,jg+=jk,jk=getchar()-'0';
     16     return jg;
     17 }
     18 int ca,ai,bi,cnt,tot,s[ss],sp,t[sj][sj],c[sj][sj];
     19 ll n,a1,a2,b[sj],pi[ss],pk[ss];
     20 ll p[sj],prime[10]={2,3,5,7,11,13,17,19,23,29},fac[ss];
     21 ll pw[ss][ss],pw1[ss];
     22 bool vi[sj];
     23 ll gcd(ll x,ll y)
     24 {
     25     if(!y)  return x;
     26     return gcd(y,x%y);
     27 }
     28 inline ll mul(ll x,ll y,ll z)
     29 {
     30     return (x*y-(ll)(((long double)x*y+0.5)/(long double)z)*z+z)%z;
     31 }
     32 ll qpow(ll x,ll y,ll z)
     33 {
     34     ll ret=1;x%=z;
     35     while(y)
     36     {
     37         if(y&1)  ret=mul(ret,x,z);
     38         y>>=1,x=mul(x,x,z);   
     39     }
     40     return ret;
     41 }
     42 ll ksm(ll x,int y)
     43 {
     44     ll ret=1;x%=mod;
     45     while(y)
     46     {
     47         if(y&1)  ret=ret*x%mod;
     48         x=x*x%mod,y>>=1;
     49     }
     50     return ret;
     51 }
     52 void pre()
     53 {
     54     for(int i=0;i<=3005;i++)
     55     {
     56         c[i][0]=1;
     57         for(int j=1;j<=i;j++)
     58         {    
     59             c[i][j]=c[i-1][j]+c[i-1][j-1];
     60             if(c[i][j]>=mod)  c[i][j]-=mod;
     61         }
     62     }
     63     for(int i=0;i<=3005;i++)
     64     {
     65         b[i]=i+1;
     66         for(int j=0;j<i;j++)
     67         {
     68             b[i]=(b[i]-c[i+1][j]*b[j])%mod;
     69             if(b[i]<0)  b[i]+=mod;
     70         }
     71         b[i]=b[i]*ksm(c[i+1][i],mod-2)%mod;
     72     }
     73     for(int i=0;i<=3005;i++)
     74     {
     75         a1=ksm(i+1,mod-2);
     76         for(int j=0;j<=i;j++)
     77             t[i][j]=c[i+1][j]*b[j]%mod*a1%mod;
     78     }
     79     for(int i=2;i<=1000;i++) 
     80         for(int j=2;j<=1000;j++)
     81             if(i*j<=1000)
     82                 vi[i*j]=1;
     83     for(int i=2;i<=1000;i++)
     84         if(!vi[i])  p[++cnt]=i;
     85 }
     86 inline ll calc(ll x)
     87 {
     88     ll ret=0,q=x%mod;
     89     ll nt=q;
     90     for(int i=bi;i>=0;i--)
     91     {
     92         ret=(ret+t[bi][i]*nt)%mod;
     93         nt=nt*q%mod;
     94     }
     95     return ret*ksm(q,bi)%mod;
     96 }
     97 bool Miller_Rabin(ll x)
     98 {
     99     if(x==1)  return 0;
    100     for(int j=0;j<=9;j++)
    101         if(x==prime[j])   
    102             return 1;
    103     ll y=x-1;
    104     int k=0;
    105     while((y&1)==0)  k++,y>>=1;
    106     for(int i=0;i<10;i++)
    107     {
    108         ll z=rand()%(x-1)+1;
    109         ll c=qpow(z,y,x),d;
    110         for(int j=0;j<k;j++)
    111         {
    112             d=mul(c,c,x);
    113             if(d==1&&c!=1&&c!=x-1)  return 0;
    114             c=d;
    115         }
    116         if(d!=1)  return 0;
    117     }
    118     return 1;
    119 }
    120 ll pollard_rho(ll x,ll y)
    121 {
    122     ll i=1,k=2,c=rand()%(x-1)+1;
    123     ll d=c;
    124     while(1)
    125     {
    126         i++;
    127         c=(mul(c,c,x)+y)%x;
    128         ll g=gcd((d-c+x)%x,x);
    129         if(g!=1&&g!=x)  return g;
    130         if(c==d)  return x;
    131         if(i==k)  d=c,k<<=1;
    132     }
    133 }
    134 void find(ll x,int tim)
    135 {
    136     if(x==1)  return;
    137     if(Miller_Rabin(x))
    138     {
    139         fac[++tot]=x;
    140         return;
    141     }
    142     ll yz=x,mk=tim;
    143     while(yz>=x)  yz=pollard_rho(yz,tim--);
    144     find(yz,mk),find(x/yz,mk);
    145 }
    146 ll solve(ll x,ll y,ll z)
    147 {
    148     ll ret=0,qwq,s1,s2;
    149     tot=sp=0;
    150     for(int j=1;j<=cnt;j++)
    151         while(x%p[j]==0)
    152             fac[++tot]=p[j],x/=p[j];
    153     find(x,120);
    154     sort(fac+1,fac+tot+1);
    155     for(int i=1;i<=tot;i++)
    156     {
    157         if(fac[i]!=fac[i-1])  sp++,pi[sp]=pk[sp]=fac[i],s[sp]=1;
    158         else  pk[sp]*=fac[i],s[sp]++;
    159     }
    160     for(int i=1;i<=sp;i++)
    161     {
    162         pw[i][0]=1,a1=ksm(pi[i],y);
    163         for(int j=1;j<=s[i];j++)
    164             pw[i][j]=pw[i][j-1]*a1%mod;
    165     }
    166     for(int i=0;i<=z;i++)
    167     {
    168         qwq=1;
    169         for(int j=1;j<=sp;j++)
    170         {
    171             s1=s2=0,pw1[0]=1,a1=ksm(pi[j],z),a2=ksm(pi[j],z-i+1);
    172             for(int k=1;k<=s[j];k++)  pw1[k]=pw1[k-1]*a2%mod;
    173             for(int k=0;k<=s[j];k++)  s1=(s1+pw[j][k]*pw1[s[j]-k])%mod;
    174             for(int k=0;k<s[j];k++)   s2=(s2+pw[j][k]*pw1[s[j]-k-1]%mod*a1)%mod;
    175             qwq=qwq*(s1-s2+mod)%mod*ksm(pk[j],z)%mod;
    176         }
    177         ret=(ret+qwq*t[z][i])%mod;
    178     }
    179     return ret;
    180 }
    181 int main()
    182 {
    183     pre();
    184     ca=read();
    185     for(int i=1;i<=ca;i++)
    186     {
    187         n=read(),ai=read(),bi=read();
    188         if(ai==bi)  printf("%lld
    ",calc(n));
    189         else        printf("%lld
    ",solve(n,ai,bi));
    190     }
    191     return 0;
    192 }
    JZPKIL
  • 相关阅读:
    optparse--强大的命令行参数处理包
    B/S和C/S架构的区别
    Document
    Document
    Document
    Document
    Document
    Document
    Document
    Document
  • 原文地址:https://www.cnblogs.com/moyiii-/p/8933742.html
Copyright © 2011-2022 走看看