思想:不直接求解,用一个序列把另一个序列表示出来。
定义 f(n)和g(n)是在正整数集合上的两个函数,如果有:
那么:
其中:
若,那么
若,任意两个不同和的为互异素数,那么
其它:
重点研究mu[]:
10以内:
与素因子快速筛相关的莫比乌斯求法:
int mu[105],pri[105],cnt; bool vis[105]; void getmu(){ mu[1]=1; for(int i=2;i<105;i++){ if(!vis[i]) { pri[cnt++]=i; mu[i]=-1; } for(int j=0;j<cnt&&pri[j]*i<105;j++){ vis[i*pri[j]]=1; if(i%pri[j]){ mu[i*pri[j]]=-mu[i]; } else { mu[i*pri[j]]=0; //具有了相同的素因子,所以对应的mu[i]=0 break; } } } }
简短的莫比乌斯求法:
void getmu(){ for(int i=1;i<105;i++){ int targe=i==1?1:0; int delta=targe-mu[i]; mu[i]=delta; for(int j=2*i;j<105;j+=i) mu[j]+=delta; } }
#include <iostream> #include <cstdio> using namespace std; //int : 2 + 9个0 const int N=1e5+10; typedef long long LL; LL phi[N],pri[N/10],cnt; bool vis[N]; void getpri(){ cnt=0; for(LL i=2;i<N;i++){ if(!vis[i]) pri[cnt++]=i; for(LL j=0;j<cnt&&i*pri[j]<N;j++){ vis[i*pri[j]]=1; if(i%pri[j]==0) break; } } } void get(){ for(int i=1;i<N;i++) phi[i]=i; for(int i=2;i<N;i++){ if(phi[i]==i){ for(int j=i;j<N;j+=i){ phi[j]=phi[j]/i*(i-1); } } } } LL fac[N],top; void solve(LL n){ top=0; for(int i=0;pri[i]*pri[i]<=n;i++){ if(n%pri[i]==0){ fac[top++]=pri[i]; while(n%pri[i]==0) n/=pri[i]; } } if(n>1) fac[top++]=n; } int main() { //freopen("cin.txt","r",stdin); LL t,a,b,c,d,k,ca=0; get(); getpri(); cin>>t; while(t--){ scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k); if(b>d){ b=b^d; d=b^d; b=b^d; } if(k==0||k>b||k>d) { printf("Case %lld: 0\n",++ca); continue; } b=b/k; d=d/k; LL ans=0; for(int i=1;i<=b;i++){ ans=ans+phi[i]; } for(int i=b+1;i<=d;i++){ solve(i); LL temp=0; for(int j=1;j<(1<<top);j++){ //二进制枚举 容斥 LL red=0,dd=1; for(int k=0;k<top;k++){ if(j&(1<<k)){ red++; dd=dd*fac[k]; } } if(red&1) temp+=b/dd; //容斥原理求非互质因子 else temp-=b/dd; } ans+=b-temp; } printf("Case %lld: %lld\n",++ca,ans); } return 0; }
关于反演的另一种解释(来自ACDREAMER大神):
推出:
将b和d都除以k处理后,1--d内所有的f(1)求出 ans1
1--b内所有的f(1)求出 ans2
ans2含有ans1中的重复部分((k1,k2)和(k2,k1)是一样的)
附图说明:
#include <iostream> #include <cstdio> using namespace std; //int : 2 + 9个0 const int N=1e5+10; typedef long long LL; LL mu[N],cnt,pri[N]; bool vis[N]; void getmu(){ cnt=0; mu[1]=1; for(int i=2;i<N;i++){ if(!vis[i]){ pri[cnt++]=i; mu[i]=-1; } for(int j=0;j<cnt&&i*pri[j]<N;j++){ if(i%pri[j]==0){ mu[i*pri[j]]=0; vis[i*pri[j]]=1; break; } else { mu[i*pri[j]]=-mu[i]; vis[i*pri[j]]=1; } } } } int main() { //freopen("cin.txt","r",stdin); LL t,a,b,c,d,k,ca=0; getmu(); cin>>t; while(t--){ scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k); if(b>d){ b=b^d; d=b^d; b=b^d; } if(k==0||k>b||k>d) { printf("Case %lld: 0\n",++ca); continue; } b=b/k; d=d/k; LL ans1=0,ans2=0; for(int i=1;i<=b;i++) ans1=ans1+(LL)mu[i]*(b/i)*(d/i); for(int i=1;i<=b;i++) ans2=ans2+(LL)mu[i]*(b/i)*(b/i); printf("Case %lld: %lld\n",++ca,ans1-ans2/2); } return 0; }