1. 程式人生 > >[BZOJ3309]DZY Loves Math(莫比烏斯反演+線性篩)

[BZOJ3309]DZY Loves Math(莫比烏斯反演+線性篩)

質因子 不存在 pan init swap i++ () 包含 love

$\sum\limits_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum\limits_{d|T}f(d)\mu(\frac{T}{d})$

求出$g(n)=\sum_{d|T}f(d)\mu(\frac{n}{d})$的前綴和,分塊加速。

考慮怎麽快速求g。觀察什麽時候d能對答案產生貢獻,顯然當且僅當:對於n的每個質因子,d包含這個質因子的次冪數至多比n包含這個質因子的次冪數少1,否則n/d就會包含平方因子。

接下來分兩種情況考慮(顯然若n只包含一個質因子則g(n)=1):

1) n中存在兩個質因子次數不同。

那麽考慮所有次數的最大值,設為k。當我們確定n中次數為k的那些因子的選取情況時(這裏選取是指是否d包含這個因子的次數比n少1),剩下的數若選取個數為奇數則$\mu$為-1否則為1。考慮到組合數的性質,n個數中選取奇數個的方案數與選取偶數個相同,所以這種情況整個的貢獻為0。

2) n中所有質因子次數相同

與上面不同的是,這裏已經不存在次數小於k的因子了。那麽,這些質因子的選取情況與$\mu$的乘積同樣由於上面那個組合數的性質,使和為0。但有一個數不同,就是當d包含的所有因子的次數都比n少1時,f(d)為k-1而不是k。綜上這部分的g(n)=(-1)^(質因子個數+1)。

考慮線性篩求g。分別記錄每個數最小因子的次數,最小因子的乘積,轉移顯然。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 typedef long long ll;
 5 using namespace std;
 6 
 7 const int N=10000010;
 8 int T,n,m,tot,b[N],p[N];
 9 ll g[N],f[N],h[N];
10 
11 void init(int n){
12     rep(i,2,n){
13         if
(!b[i]) p[++tot]=i,h[i]=1,f[i]=i,g[i]=1; 14 for (int j=1; j<=tot && p[j]*i<=n; j++){ 15 int t=p[j]*i; b[t]=1; 16 if (i%p[j]==0){ 17 h[t]=h[i]+1; f[t]=f[i]*p[j]; 18 int s=t/f[t]; 19 if (s==1) g[t]=1; else g[t]=(h[s]==h[t]) ? -g[s] : 0; 20 }else h[t]=1,f[t]=p[j],g[t]=(h[i]==1) ? -g[i] : 0; 21 } 22 } 23 rep(i,1,n) g[i]+=g[i-1]; 24 } 25 26 ll solve(){ 27 if (n>m) swap(n,m); ll res=0; 28 for (int i=1,lst; i<=n; i=lst+1) 29 lst=min(n/(n/i),m/(m/i)),res+=1ll*(n/i)*(m/i)*(g[lst]-g[i-1]); 30 return res; 31 } 32 33 int main(){ 34 freopen("bzoj3309.in","r",stdin); 35 freopen("bzoj3309.out","w",stdout); 36 init(10000000); 37 for (scanf("%d",&T); T--; ) 38 scanf("%d%d",&n,&m),printf("%lld\n",solve()); 39 return 0; 40 }

[BZOJ3309]DZY Loves Math(莫比烏斯反演+線性篩)