【Math】ACM-ICPC 2018 南京赛区网络预赛 J.Sum 线性筛法求 质因数个数 再减去平方因子个数

 

 

 

题目链接 题目  (最近真是快自闭了。。。  要找一种置之死地而后生的学习方法- 。-)

原理:

比如f(6)=4 可选的元素有2和3 现在你要把它分成两部分,那我们不妨只考虑第一部分的取法 
就是1 2 3 6,另一部分就是(6/第一部分)  (是除以)
也就是对于每个元素,可以选取或者不取
所以答案就是   2^(选项)

2^是
2的质因子个数次方  当然是经过上面处理的剩下的质因子个数  
经过我们前两步处理后,剩下的质因子个数就是可选择的质因子个数了
所谓可选择的质因子个数,就是在式子n=a*b中,可以在a,也可以在b
那么对于每个可选的质因子个数都有两种选择,所以答案就是2的可选质因子个数次方


Q:  3次的为什么就全是0了
A:  设这个质数为p,既然你已经有了一个p^3(p的3次方),那么你无论怎么分,两边终有一边的次数>=2
p^4  p^5都是0 

Q: P^2
A: p^2可以劈开成两半 然后这个质数就不能选择了,所以要减去
比如4  你只能放成2和2  1*4不行

Q: 举个例子 f(30)
A: 2*3*5 所以是8(没有被筛掉的) 
 

注意: 所谓!质因数分解!。。

36分解成的是2*2*3*3 因子个数是2个! 不是有几种表示36的方法!!!

p^1是可选择的意思是,它可以放在a,也可以放在b,所以有两种答案 对其他所有可选择的质数独立,所以答案是2的可选择的质数次

p^3不断往后累加的时候

假设这个是6^6^6 就是(6^6^6)*1  *2  *3

 

当到达*6的时候其实就是p^4

到达*36的时候就是p^5

前面置0了,后面就可以继承0的

但是可以由p^3推导

p^3不断往后累加的时候

 

总体原理:

 

模板参考: hh
 

欧拉素数筛 
复杂度 On 
其原理是每个合数都只会被一个质数筛去,因此为 On 线性筛法 
关键代码原理是 每个比 i 大的合数,必可以拆分为一个比 i 小的质数和另一个合数之积

int primes[MAXN],tot=0;
bool isPrime[MAXN];

void getPrime()
{
    memset(isPrime,true,sizeof(isPrime));
    for(int i=2;i<MAXN;i++)
    {
        if(isPrime[i])
            primes[++tot]=i;
        for(int j=1;j<=tot;j++)
        {
            if(i*primes[j]>=MAXN) break;
            isPrime[i*primes[j]]=false;
            if(i%primes[j]==0) break;//*****
        }
    }
}

 注意 判断的时候只看的是素数组成

把判断素数和   质因子分解搞在一起 高  实在是高

附赠 copy来的 打表程序


  #include<iostream>//bits/stdc++.h>
  #define ll long long
  #define maxn 50//000005
  using namespace std;
  int T,n;
  bool vis[maxn];
  int prime[maxn]; int  d[maxn];//int  num[maxn];
  int cnt;
  void getprime()//先求素因数个数
 {
     cnt=0;
     for(int i=2;i<=maxn-5;++i)
     {
         if(!vis[i])
         {//在遍历的过程中如果是1的话,那么因子个数是1,这些是没错的
             prime[++cnt]=i;
          //   num[i]=1;
             d[i]=1;
         }
		 cout << i<<"i--------"<<endl;
         for(int j=1;j<=cnt&&i*prime[j]<=maxn-5;++j)
		 {
			 cout << i * prime[j] << endl;
             vis[i*prime[j]]=1;
             if (!(i%prime[j]))// ==0  chudejin
             {
				 cout<<"除尽了!" << i << "  " << prime[j] << "  " << endl;
				 cout << " d " << i * prime[j] << "is d " << i << " " << d[i] << endl;
				 //	 cout <<"  "<<i*prime[j]<<" "<< d[i] << endl;
               //  num[i*prime[j]]=num[i]+1;
                 d[i*prime[j]]=d[i]; 
                 break;
             }
			 cout << i << "  " << prime[j] << "  " << endl;
			 cout << " d " << i * prime[j]<<"is d " << i<<"+1+1---- " << d[i] << endl;
			 //cout << d[i] << endl;
             d[i*prime[j]]=d[i]+1;
            // num[i*prime[j]]=1;
         }
     }


	 for (int i = 1; i <= maxn - 5; ++i)

		 cout << i << " " << d[i] << endl;// << num[i] << endl;
	 cout << endl;
 }
 int has[maxn],f[maxn];
 void prepare()
 {
     for(int i=1;i<=cnt;++i)
     {
         ll x=prime[i]*prime[i];
         if(x>maxn-5)break;
         x*=prime[i];
         if(x>maxn-5)break;
         for(int j=x;j<=maxn-5;j+=x)has[j]=-1;
     }
     for(int i=1;i<=cnt;++i)
     {
         ll x=prime[i]*prime[i];
         if(x>maxn-5)break;
         for(int j=x;j<=maxn-5;j+=x)if(has[j]!=-1)has[j]++;
     }
     for(int i=1;i<=maxn-5;++i)if(has[i]!=-1)
{
         int t=d[i]-has[i];
         f[i]=(1<<t);
     }
	 for (int i = 1; i <= maxn - 5; ++i) {
		 cout << "i " << i << " " << has[i]<<" "<<d[i] << endl;
		 f[i] += f[i - 1];
	 }
 }
 int main()
 {
     scanf("%d",&T);
     getprime();
     prepare();
     while(T--)
     {
         scanf("%d",&n);
cout<<f[n]<<endl;
     }
     return 0;
 }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值