BZOJ2627 JZPKIL

            一句話題意:求$\sum_{i=1}^ngcd(i,n)^xlcm(i,n)^y$,多組數據,模$1e9+7$。php

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

 

       orz出題人顧宇宙!!!orz全網題解的源頭ydc!!!感受ydc的題解已經寫得很清楚了,可是爲了內部交流仍是寫一寫……下面的公式中$[x]$表示$x==1$。ios

 

推導部分算法

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

 

Part2 套路反演函數

          這一部分咱們來整理上式最後的$\sum_{k=1,[gcd(k,\frac{n}{d})]}^{\frac{n}{d}}k^y$spa

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

 

Part3 伯努利數xml

       能夠發現Part2的最後咱們把Part1的冪之和化爲了從1開始的連續一段……這有什麼用呢?咱們能夠套結論了!有一個叫伯努利數的東西(下文推導中用$B_k$表示第$k$個伯努利數),在網上搜一搜的話資料很多,百度百科裏基本的公式也給了一些。剛開始看證實說什麼從生成函數定義的角度能夠證實它和天然數冪和的數學關係,可是我後來看到它就是在求天然數冪和的過程當中被髮明的就感受證這個不必了……求伯努利數的方法放在實現部分裏,如今咱們繼續關注推導過程。htm

$$\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^y\sum_{d|n}d^x\sum_{d_1|\frac{n}{d}}\mu(d_1)d_1^y\sum_{k=0}^yt_k(\frac{n}{dd_1})^{y-k+1}$$

$$=\sum_{k=0}^yt_kn^y\sum_{d|n}d^x\sum_{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^y\sum_{d|p_k}d^x\sum_{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$時爲一、$d_1=p_i$時爲-1而其餘狀況下都爲0,式子能夠直接變成$p_k^y\sum_{d|p_k}d^x((\frac{p_k}{d})^{y-k+1}-p_i^y\frac{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\n",calc(n));
189         else        printf("%lld\n",solve(n,ai,bi));
190     }
191     return 0;
192 }
JZPKIL
相關文章
相關標籤/搜索