[bzoj3994] [SDOI2015]约数个数和
Description
設d(x)為x的約數個數,給定N、M,求 \(\sum_{i=1}^N\sum_{j=1}^Md(ij)\)
Input
輸入文件包含多組測試數據。
第一行,一個整數T,表示測試數據的組數。
接下來的T行,每行兩個整數N、M。
Output
T行,每行一個整數,表示你所求的答案。
Sample Input
2 7 4 5 6Sample Output
110 121HINT
1<=N, M<=50000
1<=T<=50000
solution
前置知識:莫比烏斯反演
首先對于\(d\)函數有一個結論:
\[ d(ij)=\sum_{d|i}\sum_{d|j}[gcd(i,j)=1] \]
我就是因為不知道這個結論推了半個小時無果QAQ
證明大致如下:對于\(ij\)的每一個質因數\(x\),在\(i\)里出現了\(a\)次,在\(j\)里出現了\(b\)次,則總共有\(a+b+1\)中情況,
然后進行轉換,對于任意一個要選\(q\)個的情況:
- 若\(q\leqslant a\),就在\(i\)里選出\(q\)個,\(j\)里不選。
- 否則就在\(j\)里選出\(q-a\)個,這里的在\(j\)里選實質上是在\(i\)里選滿了,然后再在\(j\)里選的,為了不重復計數,在\(j\)里選\(z\)個實質上是選了\(z+a\)個。
對于每一個質因數都這么考慮,然后只要保證\(i,j\)互質即為一種方案。
然后把這個玩意帶到題目給的式子里去,大力推一下,可得:
\[ ans=\sum_{d^\prime=1}^{min(n,m)}\mu(d^\prime)\sum_{d_1=1}^{\lfloor\frac{n}{d^\prime}\rfloor}\lfloor\frac{n}{d_1d^\prime}\rfloor\sum_{d_2=1}^{\lfloor\frac{m}{d^\prime}\rfloor}\lfloor\frac{m}{d_2d^\prime}\rfloor \]
令\(f\)為式子后面那一塊,即:
\[ f(n)=\sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor \]
然后考慮下這個函數的性質:
對于枚舉的\(i\),\(\lfloor\frac{n}{i}\rfloor\)實質上就是\(n\)以內能整除\(i\)的數的個數。
反過來想,對于每個數,它的每一個約數都對答案有1的貢獻,所以\(f(n)\)等價于\(n\)以內的所有數的約數個數和。
然后線篩下約數個數和莫比烏斯函數,求下前綴和,整除分塊下,就做完了。
時間復雜度\(O(n+q\sqrt{n})\)。
#include<bits/stdc++.h> using namespace std;#define int long long void read(int &x) {x=0;int f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }void print(int x) {if(x<0) putchar('-'),x=-x;if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}const int maxn = 5e4+1;int d[maxn],pri[maxn],tot,p[maxn],vis[maxn],mu[maxn];void sieve() {d[1]=mu[1]=1;for(int i=2;i<maxn;i++) {if(!vis[i]) pri[++tot]=i,d[i]=2,p[i]=2,mu[i]=-1;for(int t,j=1;j<=tot&&i*pri[j]<maxn;j++) {vis[t=i*pri[j]]=1;if(!(i%pri[j])) {p[t]=p[i]+1;d[t]=d[i]/p[i]*p[t];mu[t]=0;break;}p[t]=2,d[t]=d[i]*p[t];mu[t]=-mu[i];}}for(int i=1;i<maxn;i++) mu[i]=mu[i]+mu[i-1];for(int i=1;i<maxn;i++) d[i]=d[i-1]+d[i]; }signed main() {sieve();int t;read(t);while(t--) {int n,m;read(n),read(m);int T=1,ans=0;if(n>m) swap(n,m);while(T<=n) {int pre=T;T=min(n/(n/T),m/(m/T));ans+=(mu[T]-mu[pre-1])*d[n/T]*d[m/T];T++;}write(ans);}return 0; }轉載于:https://www.cnblogs.com/hbyer/p/10062542.html
總結
以上是生活随笔為你收集整理的[bzoj3994] [SDOI2015]约数个数和的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法笔记 (胡凡 / 曾磊 著)
- 下一篇: ABB RAPID 程序