状态压缩的意思其实是挺简单的,就是以前在暴力解题时要开一个好大好大的数组,结果很不幸,最后发现没办法了,空间消耗太大,写法过于复杂。
然后如果使用了状态压缩之后就会发现,使用变得方便起来,而且真正消耗的空间相对于以前的数组基本上是可以忽略不计的。
但是这个还是有一定的缺陷的,因为二进制保存的长度有限,并不是说能够保存多大,大概的一个数量是20以内都没有问题,超过后就得考虑换一换方法了。这里将使用的方法的代码都保留下来。
#define LL long long
LL getans(LL num,int m)
//状态压缩,计算[1,num]中与n不互素的数的个数,m是n素因子的个数,在调用这个函数之前就是将结果全部计算出来了
{
LL ans=0,tmp,i,j,flag;
for(i=1; i<(LL)(1<<m); i++)//先乘以2^m这个数
{
tmp=1,flag=0;
for(j=0; j<m; j++)
if(i&((LL)(1<<j)))//这是一个进行位运算的过程
flag++,tmp*=prime[j];//flag是用来计算到底有几重计算的结果了,而tmp是用来反映
if(flag%2==1)//奇数加,偶数减
ans+=num/tmp;//计算与n有公共因子的数的个数
else
ans-=num/tmp;
}
return ans;
}
//在这里现将我在网上百度到的一段代码写出来,并讲讲自己的看法,最后在自己书写出自己的代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
#define maxn 70
LL prime[maxn];//对于一些比较大的数组一般是开在main函数外面的,这样的使用才不会出问题
LL make_ans(LL num,int m)
{
LL ans=0,tmp,i,j,flag;
for(i=1;i<(LL)(1<<m);i++) //用二进制来1,0来表示第几个素因子是否被用到,如m=3,三个因子是2,3,5,则i=3时二进制是011,表示第2、3个因子被用到
{
tmp=1,flag=0;
for(j=0;j<m;j++)
if(i&((LL)(1<<j)))//判断第几个因子目前被用到
flag++,tmp*=prime[j];
if(flag&1)//容斥原理,奇加偶减
ans+=num/tmp;
else
ans-=num/tmp;
}
return ans;
}
int main()
{
int T,t=0,m;
LL n,a,b,i;
//对于这样的定义方式我不是特别的赞同,因为全部写在循环外面虽然是方便的变量的类型的定义,但是在使用时可能就不清楚它本身的含义了,可以借鉴
scanf("%d",&T);
while(T--)
{
scanf("%I64d%I64d%I64d",&a,&b,&n);//这里是将测试数据输入
m=0
for(i=2;i*i<=n;i++)
//对n进行素因子分解 ,学姐说开方后最多只会漏掉一个解的情况,那就是它本身,后面的if就是对这种情况的一个判断
if(n&&n%i==0)
{
prime[m++]=i;
while(n&&n%i==0)
n/=i;
} //这个分解过程其实也是非常简单的,主要是自己动手将整个过程写一写就会发现是将n的所有约数(除本身)保存在一个数组中
if(n>1)
prime[m++]=n;//后面的++是用来计数的,刚刚好弥补了数组从0开始使用的一个空缺
printf("Case #%d: %I64d\n",++t,(b-make_ans(b,m))-(a-1-make_ans(a-1,m)));
//最后是减法的原因非常简单,题目要求求解 的结果是互质,但是我们通过函数求解得到的是相反的结果,所以通过减法求得
}
return 0;
}
状态压缩与容斥原理