组合数学[包容排斥原理]

                                                                      电子科技大学  无平方因子数

Description

无平方因子数即对于任意一个素数p,p^2都不会整除那个数,如1 , 5=5 , 15=3*5都是无平方因子数,而20=2^2*5不是。现在给定一个n (1 <= n < 10^12) ,求区间[1,n]中无平方因子数的个数。

Input

第一行有个整数T,代表数据组数(T <= 10)
接下来有T行,每行有个整数n (1 <= n < 10^12) 

Output

输出T行,每行输出一个整数代表区间[1,n]内的无平方因子数的个数。

Sample Input

3
1
10
30

Sample Output

1
7
19

 

这道题是在白书上看到的,但是白书上介绍的算法是用类素数筛法的方法解决。但是这里的数据太大了,显然是不能够解决的。于是要领想他法,后来才发现这是可以用离散数学里的包含排斥原理。就是数论中的容斥原理。

本人表示刚学过离散,所以这里就以课本里的包含排斥原理给大家讲解如何解该道题把.

 直接给出离散书中的公式吧:

         S- |Pi|(1<= i <= n)+|Pi&&Pj|(1<=i < j<= n)-......+(-1)^n|Pi &&Pj&&.....Pn|

其中公式的意思是,表示事件Pi发生,Pi&&Pj表示事件Pi和事件Pj同时发生。

 

#include <iostream>
#include <algorithm>  
#include <cstdio>  
#include <cstring>  
using namespace std;  
  
#ifdef __int64  
      typedef __int64 LL;  
#else  
     typedef long long LL;  
#endif  
const int N = 1e6 + 5;  
int top,prime[N];  
  
void IsPrime()  
{  
    bool vst[N];  
    memset(vst,0,sizeof(vst));  
    for(int i = 2;i < N;++i)if(!vst[i])  
    {  
        prime[top++] = i;  
        for(int j = i+i;j < N;j += i)  
          vst[j] = 1;  
    }  
}  
LL Solve(int r,LL num)  
{  
    LL res = 0;  
    for(int i=r;(i<top)&&(LL)prime[i]*prime[i]<=num;++i)  
    {  
        //LL tmp = prime[i]*prime[i];  
        res += num/prime[i]/prime[i] - Solve(i+1,num/prime[i]/prime[i]);// S - |Ai|(1<=i<=n) + |Ai&&Aj|(1<=i<j<=n)-...  
    }  
    return res;  
}  
int main()  
{  
    IsPrime();  //cout<<top<<endl;  
    int T;  
    LL n;  
    scanf("%d",&T);  
    while(T--)  
    {  
        scanf("%lld",&n);  
        printf("%lld\n",n-Solve(0,n));  
    }  
    return 0;  
}  

 

 M^k数     ||   HDU 2204  Eddy's爱好

 

时间限制: 1000  ms  |  内存限制: 65535  KB
难度: 4
描述
给你一个数n,请算出1~n之间有多少个整数能表示为M^k(k>1,m,k都是正整数)形式的个数。
输入
每行一个整数n(1<=n<=10^18)。
输出
输出1~n符合条件的数的个数。
样例输入
4
36
1000000000000000000
样例输出
2
9
1001003332


 

 

  幸好在离散里,自己偷学了包含排斥公式,因此看到的时候不会一头雾水。

题目分析:

          既然是中文就没有什么可解释的了。自己看吧。

思路分析:

          我们可以从题目给出的数据看到,n的范围是64位的,而不像电子科技大学的那到题一样可以打表过。因为当1*10^8的时候素数已经到达了1*10^7显然是TEL。所以,要另想他法。最后。。。。。我是没想到可以用包容排斥原理,是偷看了别人的博客才知道的,囧!!因为,这道题涉及了一个我还没学过的数学知识。下面就讲一下该题主要涉及了那些知识吧。

1.一个数的x = n^(1/p)表示n中有x个p次方组成。

2.任意一个数n可以表示成由n^P组成的形式。

求解过程,我们可以根据上一题给出的包含排斥原理,来算出结果。先算出一个素数的情况,然后是两个,最后是三个,因为

2*3*5 = 60 ,而2^60 > 10^18所以只要算到三个素数的情况就好了。

 
#include <stdio.h>
#include <math.h>
typedef long long LL;
const double EPS = 1e-8;
const int prime[20] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};
int main()
{
//    printf("%lf\n",EPS);
    LL n;
    while(~scanf("%lld",&n))
    {
        int i,j,k,ans = 1;
        LL tmp;
        for(i = 0;i <= 16;i++)
        {
            tmp = (long long)(pow(double(n),(1.0/(1.0*prime[i])))+EPS);
            if(tmp == 1)break;
            ans += tmp - 1;
        }
        for(i = 0;i <= 16;++i)
          for(j = i+1;j <= 16;++j)
          {
              tmp = (long long)(pow(double(n),(1.0/(1.0*prime[j]*prime[i])))+EPS);
              if(tmp == 1) break;
              ans -= tmp - 1;
          }
        for(i = 0;i <= 16;++i)
          for(j = i+1;j <= 16;++j)
            for(k = j+1;k <= 16;++k)
            {
                tmp = (long long)(pow(double(n),(1.0/(1.0*prime[i]*prime[j]*prime[k])))+EPS);
                if(tmp == 1) break;
                ans += tmp - 1;
            }
        printf("%d\n",ans);
    }
    return 0;
}
        


 

 

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值