• 状态压缩与容斥原理


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

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

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

    我要坚持一年,一年后的成功才是我想要的。
  • 相关阅读:
    LeetCode 773. Sliding Puzzle
    oracle latch工作原理
    Oracle关于锁的几种类型和参数
    Java的反射机制
    JAVA多线程与并发学习总结
    Spring 概念详解
    Spring的AOP
    spring面试题 对DI , AOP概念的理解
    双11的架构
    Java线程的定义
  • 原文地址:https://www.cnblogs.com/tianxia2s/p/3858282.html
Copyright © 2020-2023  润新知