【普通版】首先是一道单纯的容斥题目(hdu4135):
求[a,b]内与n互质的数的个数,n<=10^9,a,b<=10^14.
可以先用容斥求出[a,b]与n不互质的数的个数,再相减。
#include<bits/stdc++.h> #define ll long long using namespace std; int main (){ int t,cnt=0; cin>>t; while(t--){ ll a,b,n; scanf("%I64d%I64d%I64d",&a,&b,&n); vector<int> v; for(int i=2;i*i<=n;++i){ if(n%i==0){ v.push_back(i); while(n%i==0) n/=i; } } if(n>1) v.push_back(n); ll s1=0,s2=0; a--; for(int i=1;i<(1<<v.size());++i){ ll s=1,p=0; for(int j=0;j<v.size();++j){ if(i&(1<<j)){ s*=v[j]; p++; } } if(p%2==1){ s1+=a/s; s2+=b/s; } else{ s1-=a/s; s2-=b/s; } } printf("Case #%d: %I64d\n",++cnt,(b-s2)-(a-s1)); } return 0; }
【加强版】(hdu1695)在[1,b]中取一个数x,[1,d]中取一个数y,满足gcd(x,y)=k。求(x,y)的对数。bdk<=10^5,ac=1
这题可以有三种做法:[直接对质因子容斥]或者[欧拉函数优化+容斥]或者[莫比乌斯反演]。最后一种效率最高。
用欧拉函数做的那种方法可以参考上一个题解。由于k是定值所以不用枚举了。gcd(x/k,y/k)=1.
【直接对质因子容斥】
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; #define N 100005 typedef long long ll; vector<int> x[N]; bool is[N]; void prime(){ memset(is,false,sizeof(is)); for(int i=0;i<N;i++) x[i].clear(); for(int j=2;j<N;j+=2) x[j].push_back(2); for(int i=3;i<N;i+=2) if(!is[i]){ for(int j=i;j<N;j+=i){ is[j]=true; x[j].push_back(i); } } } int work(int u,int s,int w){ int cnt=0,v=1; for(int i=0;i<x[w].size();i++){ if((1<<i)& s){ cnt++; v*=x[w][i]; } } int all=u/v; if(cnt%2==0)return -all; else return all; } int main(){ prime(); int T,a,b,c,d,k; scanf("%d",&T); for(int cas=1;cas<=T;cas++){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); if(k==0){ printf("Case %d: 0\n",cas); continue; } b /= k,d /= k; if(b>d){ a=b;b=d;d=a;} long long ans=0; for(int i=1;i<=d;i++){ k=min(i,b); ans+=k; for(int j=1;j<(1<<x[i].size());j++) ans-=work(k,j,i); } printf("Case %d: %I64d\n",cas,ans); } return 0; }【欧拉函数优化+容斥】
#include<bits/stdc++.h> using namespace std; const int Max=100005; __int64 elur[Max];//存放每个数的欧拉函数值 vector<int> p[Max];//存放数的素因子 void init(){//筛选法得到数的素因子及每个数的欧拉函数值 elur[1]=1; for(int i=2;i<Max;i++){ if(!elur[i]){ for(int j=i;j<Max;j+=i){ if(!elur[j]) elur[j]=j; elur[j]=elur[j]*(i-1)/i; p[j].push_back(i); } } elur[i]+=elur[i-1]; //进行累加(法里数列长度) } } int main(){ int t,a,b,c,d,k; init(); scanf("%d",&t); for(int ca=1;ca<=t;ca++){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); printf("Case %d: ",ca); if(k==0){ printf("0\n"); continue; } if(b>d) swap(b,d); b/=k; d/=k; __int64 ans=elur[b]; for(int i=b+1;i<=d;i++){ ans+=b; for(int j=1;j<(1<<p[i].size());++j){ //求不大于b的数中,与i不互质的数的个数 int s=1,w=0; for(int k=0;k<p[i].size();++k){ if(j&(1<<k)){ s*=p[i][k]; w++; } } if(w%2==1) ans-=b/s; else ans+=b/s; } } printf("%I64d\n",ans); } return 0; }【莫比乌斯反演】
#include<bits/stdc++.h> using namespace std; const int MAXN=1000000; bool check[MAXN+10]; int prime[MAXN+10]; int mu[MAXN+10]; void Moblus(){ memset(check,false,sizeof(check)); mu[1]=1; int tot=0; for(int i=2;i<=MAXN;i++){ if( !check[i] ){ prime[tot++]=i; mu[i]=-1; } for(int j=0;j<tot;j++){ if(i*prime[j]>MAXN)break; check[i*prime[j]]=true; if( i%prime[j]==0){ mu[i*prime[j]]=0; break; } else{ mu[i*prime[j]]=-mu[i]; } } } } int main(){ int T; int a,b,c,d,k; Moblus(); scanf("%d",&T); int iCase=0; while(T--){ iCase++; scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); if(k==0){ printf("Case %d: 0\n",iCase); continue; } b /= k; d /= k; if(b>d)swap(b,d); long long ans1=0; for(int i=1;i<=b;i++) ans1+=(long long)mu[i]*(b/i)*(d/i); long long ans2=0; for(int i=1;i<=b;i++) ans2+=(long long)mu[i]*(b/i)*(b/i); ans1-=ans2/2; printf("Case %d: %I64d\n",iCase,ans1); } return 0; }