题目链接:点击打开链接
题目大意:求出a~b中与n互质的数字的个数
题目思路:菜鸡之前不会容斥原理以及快速将一个数字分解成质因数..流下了没有技术的泪水。这道题首先先转换一下,从求a~b中与n互质的数字个数转换成求1~b中与n互质的数字个数-1~a-1中与n互质的数字个数。然后互质的个数是由数字总数-与n不互质的数字,比如1~b中与n互质的数字个数=b-1~b中与n不互质的数字个数。
第一步,求出n的质因数。为什么要求呢?先别急第二步讲怎么用n的质因数,这里先讲求法。就是i从2开始,如果i*i大于n就停止,然后遇到n%i以后,在n%i==0的情况下疯狂除i,保证剩下的数字不可以再被i整除,同时此时i是n的一个质因数。
第二步,求1~b中与n不互质的数字个数。与n不互质的数字一定有一个特点,那就是这些数字的质因数一定跟n的质因数有公共部分。我们知道,1~b中能被x整除的数字总数为x/b,比如6/3=2,符合要求的数字为3 6,6/2=3,符合要求的数字为2 4 6,然后根据容斥原理,我们需要奇加偶减,因为如果我们只是单纯的把x/b全加起来,会导致有公共部分加了两次,导致结果错误,需要把这部分去掉,所以要用到容斥原理的性质。容斥原理一共有三种实现方法,dfs,队列数组,位运算。这里采用位运算。
位运算的核心代码是
ll solve(ll num){
ll temp,ans=0,flag;
for(ll i=1;i<1<<m;i++){
temp=1,flag=0;
for(ll j=0;j<m;j++){
if(i&1<<j){
temp*=prime[j],flag++;
}
}
if(flag&1){//奇加偶减
ans+=num/temp;
}
else{
ans-=num/temp;
}
}
return ans;
}
位运算看的确实特别令人头大..我找了很多东西还是没看懂是怎么实现的,但是我知道这东西做了什么事情..根据容斥原理,当两个数字时,A+B-AB,三个数字时,A+B+C-AB-AC-BC+ABC,分别是3项和7项,没错,2^k-1项,所以i从1遍历到2^m-1处,flag记录几个数字相乘。然后里面那层i&1<<j配合从0遍历到m好像直接能够实现所有容斥原理所需要的那种搭配,虽然不知道为啥,记住好了..
第三步,b-1~b中跟n不互质的数字个数-(a-1-1~a-1中跟n不互质的数字个数)就美滋滋啦(别忘了输出case #1...)
以下是代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll prime[1005],m;
void qiuyin(ll n){
m=0;
for(ll i=2;i*i<=n;i++){
if(n%i==0){
while(n%i==0){
n/=i;
}
prime[m++]=i;
}
}
if(n>1){
prime[m++]=n;
}
}
ll solve(ll num){
ll temp,ans=0,flag;
for(ll i=1;i<1<<m;i++){
temp=1,flag=0;
for(ll j=0;j<m;j++){
if(i&1<<j){
temp*=prime[j],flag++;
}
}
if(flag&1){
ans+=num/temp;
}
else{
ans-=num/temp;
}
}
return ans;
}
int main(){
int t,tot=1;
scanf("%d",&t);
while(t--){
ll a,b,n;
scanf("%lld%lld%lld",&a,&b,&n);
qiuyin(n);
printf("Case #%d: %lld\n",tot++,((b-solve(b))-(a-1-solve(a-1))));
}
return 0;
}