1. 程式人生 > >【51nod】1222 最小公倍數計數 莫比烏斯反演+組合計數

【51nod】1222 最小公倍數計數 莫比烏斯反演+組合計數

ace using 復雜度 amp nebula names ons 問題 sin

【題意】給定a和b,求滿足a<=lcm(x,y)<=b && x<y的數對(x,y)個數。a,b<=10^11。

【算法】莫比烏斯反演+組合計數

【題解】★具體推導過程參考:51nod1222 最小公倍數計數

過程運用到的技巧:

1.將所有i和j的已知因子提取出來壓縮上屆。

2.將帶有μ(k)的k提到最前面,從而後面變成單純的三元組形式。

最終形式:

$$ans=\sum_{k=1}^{\sqrt n} \mu(k) \sum_{d} \sum_{i} \sum_{j} [i*j*d<=\frac{n}{k^2}]$$

問題轉化為枚舉(d,i,j)三元組滿足其乘積<=n/k^2。

雖然題目要求組合(有序),但是有d的存在,所以先求排列(無序)。

但是三元組的排列不方便統計,所以求三元組的組合乘上排列系數

先算嚴格從小到大的,然後減去兩個相同的和三個相同的。

最後+n後/2就變成組合。

聽說復雜度O(n^(2/3))。

技術分享圖片
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
const int maxn=1000010;
int miu[maxn],prime[maxn],tot;
bool vis[maxn];
int solve(int x){
    if
(!x)return 0; int N=(int)sqrt(x)+1,ans=0; for(int k=1;k<=N;k++)if(miu[k]){ int n=x/(k*k); for(int d=1;d*d*d<=n;d++){ for(int i=d+1;i*i*d<=n;i++)ans+=miu[k]*((n/(d*i)-i)*6+3); ans+=miu[k]*((n/(d*d)-d)*3+1); } } return (ans+x)/2; }
#undef int int main(){ #define int long long int A,B; scanf("%lld%lld",&A,&B); int N=(int)sqrt(B)+1; miu[1]=1; for(int i=2;i<=N;i++){ if(!vis[i]){miu[prime[++tot]=i]=-1;} for(int j=1;j<=tot&&i*prime[j]<=N;j++){ vis[i*prime[j]]=1;// if(i%prime[j]==0)break; miu[i*prime[j]]=-miu[i]; } } printf("%lld\n",solve(B)-solve(A-1)); return 0; }
View Code

【51nod】1222 最小公倍數計數 莫比烏斯反演+組合計數