BZOJ2749: [HAOI2012]外星人
2749: [HAOI2012]外星人
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 377 Solved: 199
[Submit][Status]
Description
Input
Output
输出test行,每行一个整数,表示答案。
Sample Input
1
2
2 2
3 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
代码:求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 }
其实f[x]=求2的个数,这里只是用了不同的方法。