BZOJ2749: [HAOI2012]外星人

2749: [HAOI2012]外星人

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 377  Solved: 199
[Submit][Status]

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

 

Source

题解:

终于把这题搞掉了。。。

研究了一下此题的两种解法。

一种是直接求 这个数一直phi,最后能phi出多少个2,就是答案。

一种是利用递推的思想,用 f[i]表示i phi几次能变成1,有递推式 f[i]=f[phi(i)]+1

这两种方法都可以求出正确结果,让我们讨论一下为什么这样就可以:

首先,

题中给出了这样的公式,然后我们发现每次phi只能使每个质数的指数-1,然后这个 p[i]-1会继续质因数分解然后加在其它比它小的质数的指数上。

然后我们就会发现,2被phi的次数一定是最多的!!!

假设还有另一个质数 x 那么 phi(x)会多出1个2,所以 phi(2)的次数>=phi(x) 的次数!

所以 2被phi了多少次,ans就是多少!2还没有被phi完,其他质数的质数就已经都为0了!

然后呢?我们得到了一个什么结论?一个数被phi成1的次数就等于它phi了多少次2

这样的话 f[x]就等于 x phi 2的次数。

然后两种方法就统一了。

这也就解释了为什么不同的质数之间的被phi的次数是可以叠加的,因为我们加的实际上是同一个质数2的次数,而phi每次只能让2的指数-1!!!

还有一些细节要注意,这里就不提出了。

代码:直接递推求 f[x](求phi写萎了。。。)(这里面的偶数求的会比实际少1,因为并没有计入第一次phi的2)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int m=200010;
 7 int p[m],f[m],n,t,i,j,x,y;
 8 long long ans; 
 9 int main()
10 {
11     for(i=1;i<=m;i++) p[i]=i;
12     for(i=2;i<=m;i++)
13         if(p[i]==i)
14             for(j=i;j<=m;j+=i) p[j]=p[j]/i*(i-1);
15     p[1]=1,f[1]=-1;
16     for(i=2;i<=m;i++) f[i]=f[p[i]]+1;
17     f[1]++,f[2]++; 
18     cin>>t;
19     while(t--)
20     {
21         scanf("%d",&n);
22         for(ans=1,i=1;i<=n;i++)
23         {
24             scanf("%d%d",&x,&y);
25             if(x==2) ans--;
26             ans+=(long long)f[x]*y;
27         }
28         printf("%lld\n",ans);
29     }
30     return 0;
31 }
32 
View Code

代码:求2的个数

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<iostream>
 7 #include<vector>
 8 #include<map>
 9 #include<set>
10 #include<queue>
11 #include<string>
12 #define inf 1000000000
13 #define maxn 100000+5
14 #define maxm 500+100
15 #define eps 1e-10
16 #define ll long long
17 #define pa pair<int,int>
18 #define for0(i,n) for(int i=0;i<=(n);i++)
19 #define for1(i,n) for(int i=1;i<=(n);i++)
20 #define for2(i,x,y) for(int i=(x);i<=(y);i++)
21 #define for3(i,x,y) for(int i=(x);i>=(y);i--)
22 #define mod 1000000007
23 using namespace std;
24 inline int read()
25 {
26     int x=0;char ch=getchar();
27     while(ch<'0'||ch>'9'){ch=getchar();}
28     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
29     return x;
30 }
31 int tot,p[maxn],f[maxn];
32 bool v[maxn];
33 int main()
34 {
35     freopen("input.txt","r",stdin);
36     freopen("output.txt","w",stdout);
37     f[1]=1;
38     for2(i,2,maxn)
39      {
40          if(!v[i]){p[++tot]=i;f[i]=f[i-1];}
41          for1(j,tot)
42           {
43               int t=i*p[j];
44               if(t>maxn)break;
45               v[t]=1;
46               f[t]=f[i]+f[p[j]];
47               if(i%p[j]==0)break;
48           }
49      }
50     int m=read();
51     while(m--)
52      {
53          int n=read();ll ans=1;
54          for1(i,n) 
55          {
56              int x=read(),y=read();
57              if(x==2)ans--;
58             ans+=(ll)f[x]*y;
59         }
60         printf("%lld\n",ans);
61     }
62     return 0;
63 }
View Code

其实f[x]=求2的个数,这里只是用了不同的方法。

posted @ 2014-10-07 10:53  ZYF-ZYF  Views(299)  Comments(0Edit  收藏  举报