题意:求区间 [a~b]内与n互素的数的个数
分析:
容斥原理很容易想到,关键在于怎么实现。
有三种方法:dfs、队列数组、位运算。我觉得位运算比较好理解。先求出n的质因数设有cnt个,则这cnt个数有2^cnt-1种组合。用二进制位很容易证明:设有三个质因数作为例子,则 001,010,100,011,101,110,111,就分别表示了选一个,选两个,选三个的各种情况,这样就很明显了,共有 2^3-1 也就是 7 种,所以我们可以用某一位是否为1来判断是否选择这个质因数。那么问题又来了,如何用位来实现呢,这里就用到了位运算技巧:预算符 & 就可以判断某一位是否为1。上面已经证明组合有2^cnt-1种,也就是情况要讨论这么多次,所以我们就在每种情况下用运算符 & 判断是否这种情况的时候选择了这个质因数。若选择了那么计数器加1,且把这个数乘进去。具体看代码
另外,容斥原理有一篇不错的文章,先mark:http://www.cppblog.com/vici/archive/2011/09/05/155103.html
代码:
#include<iostream>
using namespace std;
int t;
long long a,b,n;
long long prim[1000010];
long long s1,s2;
int cnt;
void init() //求n的质因数,用的是上一篇博文的方法一
{
for(int i=2;i*i<=n;i++){
if(n%i==0){
prim[cnt++]=i;
while(n%i==0){
n/=i;
}
}
}
if(n>1) prim[cnt++]=n;
}
long long work(long long m)
{
long long sum=0;
for(int i=1;i<(1<<cnt);i++){
int mul=1;
int tot=0;
for(int j=0;j<cnt;j++){
if(i&(1<<j)){ //位运算判断当前情况是否选择了这个质因数,若不理解手动试一试
tot++;
mul*=prim[j];
}
}
if(tot&1) sum+=m/mul;
else sum-=m/mul;
}
return sum;
}
int main()
{
cin>>t;
int cas=1;
while(t--){
cin>>a>>b>>n;
cnt=0;
init();
s1=work(a-1);
s2=work(b);
cout<<"Case #"<<cas<<": ";
cout<<b-a+1-(s2-s1)<<endl;
cas++;
}
}