人生列车

follow on!success!

导航

状态压缩与容斥原理

状态压缩的意思其实是挺简单的,就是以前在暴力解题时要开一个好大好大的数组,结果很不幸,最后发现没办法了,空间消耗太大,写法过于复杂。

然后如果使用了状态压缩之后就会发现,使用变得方便起来,而且真正消耗的空间相对于以前的数组基本上是可以忽略不计的。

但是这个还是有一定的缺陷的,因为二进制保存的长度有限,并不是说能够保存多大,大概的一个数量是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;
}

 

//自己经过修改后ac的代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
#define MAX 100 //这个数组是不可能大起来的,因为数据也就是10^9,如果是每个都用到的话是根本不可能的

LL prime [MAX];
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,m=0;

cin>>T;
for(int j =1;j <= T; j++,m=0)
{
LL n,a,b;
cin>>a>>b>>n;

for(LL i=2;i*i<=n;i++) //对n进行素因子分解
if(n&&n%i==0)
{
prime[m++]=i;
while(n%i==0)
n/=i;
}
if(n>1)
prime[m++]=n;
cout<<"Case #"<<j<<": "<<(b-make_ans(b,m))-(a-1-make_ans(a-1,m))<<endl;
}
return 0;
}

 

//修改

#include <iostream>
#include <vector>
using namespace std;
#define LL long long

//程序实现:求解从1到r之间与n互素的数
LL solve (LL n, LL r) {
vector<LL> p;
for (LL i=2; i*i<=n; ++i)
if (n % i == 0) {
p.push_back (i);
while (n % i == 0)
n /= i;
}
if (n > 1)
p.push_back (n);
LL sum = 0;
for (LL msk=1; msk<(1<<p.size()); ++msk) {
LL mult = 1,
bits = 0;
for (LL i=0; i<(LL)p.size(); ++i)
if (msk & (1<<i)) {
++bits;
mult *= p[i];
}
LL cur = r / mult;
if (bits % 2 == 1)
sum += cur;
else
sum -= cur;
}
return r - sum;
}

int main()
{
int T;
cin>>T;
for(LL i = 1; i <= T; i++){
LL a,b,n;
cin>>a>>b>>n;
cout<<"Case #"<<i<<": "<<(solve(n,b) - solve(n,a-1))<<endl;
}
return 0;
}

posted on 2014-07-21 13:57  tianxia2s  阅读(286)  评论(0编辑  收藏  举报