【BZOJ2749】【HAOI2012】外星人[欧拉函数]

外星人

Time Limit: 3 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

Description

  

Input

   

Output

  输出test行,每行一个整数,表示答案。

Sample Input

  1
  2
  2 2
  3 1

Sample Output

  3
  

HINT

  Test<=50 Pi<=10^5,1<=Q1<=10^9

Main idea

  给定一个数,用Πp[i]^q[i](p<=10^5,q<=10^9)的形式表示,问最少需要对这个数字x进行几次x=Φ(x)操作使得x=1。

Solution

  这显然是一道数论题。
  首先想到了只有Φ(2)=1,所以最后答案必然需要转成带2的形式,我们先考虑一个数字,由欧拉函数的推导公式Φ(Πp[i]^q[i])=Π(p[i]-1)*p[i]^(q[i]-1)可以发现每次求Φ会消去一个质因数2,并且产生若干个2(产生的2是有上限的)。
  这句话是什么意思呢?
  我们举个例子:讨论一个偶数180=2^2 * 3^2 * 5,Φ(180)=2^1 * (3-1)*3 * (5-1)=48,这里产生了3个2,消去了1个2。
  所以我们只要求出产生了几个2即可(由于除了Φ(2)以外的数都是偶数,所以任意奇数只要经过一遍求Φ就可以变为偶数来处理,次数+1),因为每次只能消去一个1,所以答案就应该是这个数分解出的2的个数。
  知道欧拉函数是一个积性函数,并且我们现在求的显然是一个完全积性函数,由于这个性质,求分解出几个2可以使用线性筛来实现,对于每一项p[i]^q[i]分解出的个数就是(p[i]分解出的个数*q[i]),答案就是Σ(每一项分解出的个数)

Code

 1 #include<iostream>  
 2 #include<algorithm>  
 3 #include<cstdio>  
 4 #include<cstring>  
 5 #include<cstdlib>  
 6 #include<cmath>
 7 #include<queue>
 8 using namespace std;  
 9         
10 const int ONE=100001;
11  
12 int T;
13 int f[ONE],p[ONE],tot,phi[ONE];
14 int x,y,m,PD;
15 long long Ans;
16  
17 int get()
18 {
19         int res,Q=1;    char c;
20         while( (c=getchar())<48 || c>57)
21         if(c=='-')Q=-1;
22         if(Q) res=c-48; 
23         while((c=getchar())>=48 && c<=57) 
24         res=res*10+c-48; 
25         return res*Q; 
26 }
27  
28 void Get_f(int n)
29 {
30         phi[1]=1;
31         for(int i=2;i<=n;i++)
32         {
33             if(!f[i]) 
34             {   
35                 p[++tot]=i;
36                 phi[i]=phi[i-1];
37             }
38              
39             for(int j=1;j<=tot;j++)
40             {
41                 if(i*p[j]>n) break;
42                 f[i*p[j]]=1;
43                 phi[i*p[j]]=phi[i]+phi[p[j]];
44                 if(i%p[j]==0) break;
45             }
46         }
47 }
48  
49 int main()
50 {
51         Get_f(ONE-1);
52         T=get();
53         while(T--)
54         {
55             m=get();
56             Ans=PD=0; 
57             for(int i=1;i<=m;i++)
58             {
59                 x=get();    y=get(); 
60                 Ans+=(long long)phi[x]*y;
61                 if(!PD && x==2) PD=1;
62             }
63             printf("%lld\n",Ans+(!PD));
64         }
65 }
View Code

 

转载于:https://www.cnblogs.com/BearChild/p/6441418.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值