质因子和容斥原理
算一个数字m,1~n之间有多少个与n互质的数
把n的素因子提取,进行二进制枚举,容斥
模板如下
il void pan(ll k){ cnt=0; ll kk=sqrt(k); for(ll i=2;i<=kk;i++){ if(k%i==0){ s[cnt++]=i; while(k%i==0){k/=i;} } } if(k!=1){ s[cnt++]=k; } return; } ll all(ll k){ ll ans=0; for(ll i=1;i<((ll)1<<cnt);i++){ ll sum=1,num1=0,tmp; for(ll j=0;j<cnt;j++){ if(i&((ll)1<<j)){ sum*=s[j];num1++; } } tmp=k/sum; if(num1&1){ans+=tmp;} else{ ans-=tmp; } } return k-ans; }
hdu4135
题意:输入n,m,k,在区间n~m中有多少跟k互质的数
思路:就是模板。
#include<bits/stdc++.h> using namespace std; #define ll long long #define il inline #define it register int #define lowbit(x) (x)&(-x) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 1000000007 const int maxn=1e5+10; ll n,m,k2; int t; int cnt; ll s[100]; il void pan(ll k){ cnt=0; ll kk=sqrt(k); for(ll i=2;i<=kk;i++){ if(k%i==0){ s[cnt++]=i; while(k%i==0){k/=i;} } } if(k!=1){ s[cnt++]=k; } return; } ll all(ll k){ ll ans=0; for(ll i=1;i<((ll)1<<cnt);i++){ ll sum=1,num1=0,tmp=i; for(ll j=0;j<cnt;j++){ if(i&((ll)1<<j)){ sum*=s[j];num1++; } } tmp=k/sum; if(num1&1){ans+=tmp;} else{ ans-=tmp; } } return k-ans; } int main(){ it cc=1; scanf("%d",&t); while(t--){ scanf("%lld%lld%lld",&n,&m,&k2); pan(k2);//cout<<all(2)<<endl; printf("Case #%d: %lld\n",cc++,all(m)-all(n-(ll)1)); } return 0; }
cf一道D题
题意:输入n,m,gcd(n,m)=gcd(n+x,m)的x有多少个,x的范围是0~m-1
思路:k=gcd(n,m),n=k*a1,m=k*a2,n+x=k*a3
a1,a2,a3必定互质
就是求 n/k ~ (n+m-1)/k之中有多少跟 m/k 互质的数
#include<bits/stdc++.h> using namespace std; #define ll long long #define il inline #define it register int #define lowbit(x) (x)&(-x) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 1000000007 const int maxn=1e5+10; ll n,m; int t; int cnt; ll s[100]; il void pan(ll k){ cnt=0; ll kk=sqrt(k); for(ll i=2;i<=kk;i++){ if(k%i==0){ s[cnt++]=i; while(k%i==0){k/=i;} } } if(k!=1){ s[cnt++]=k; } return; } ll all(ll k){ ll ans=0; for(ll i=1;i<((ll)1<<cnt);i++){ ll sum=1,num1=0,tmp=i; for(ll j=0;j<cnt;j++){ if(i&((ll)1<<j)){ sum*=s[j];num1++; } } tmp=k/sum; if(num1&1){ans+=tmp;} else{ ans-=tmp; } } return ans; } int main(){ scanf("%d",&t); while(t--){ scanf("%lld%lld",&n,&m); ll k0=__gcd(n,m); ll k1=n/k0; ll k2=m/k0;//cout<<k1<<k2<<endl; pan(k2); printf("%lld\n",k2-all(k2+k1-(ll)1)+all(k1-(ll)1)); } return 0; }
欧拉函数 φ(n) 是小于等于 n 且与 n 互素的正整数的个数
n=p1^a1*p2^a2…pk^ak (p是质素)
就有 φ(n)=n*( 1 - 1/p1 )*( 1 - 1/p2 )…( 1 - 1/pk)
upd:
看了别人的博客,发现 D 题居然就是欧拉函数模板,好神奇.jpg!!!
让我好好想想
upd2:
n~n+m-1%m等效于0~m-1,就是求欧拉函数
#include<bits/stdc++.h> using namespace std; #define ll long long #define il inline #define it register int #define lowbit(x) (x)&(-x) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 1000000007 const int maxn=2e5+10; ll n,t,m; int main(){ scanf("%lld",&t); while(t--){ scanf("%lld%lld",&n,&m); ll k=__gcd(n,m); n/=k,m/=k; ll ans=m; for(ll i=2;i*i<=m;i++){ if(m%i==0){ while(m%i==0){m/=i;} ans=ans/i*(i-1); } } if(m!=1){ ans=ans/m*(m-1); } printf("%lld\n",ans); } return 0; }