BZOJ2749: [HAOI2012]外星人

时间:2022-03-01 17:27:57

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

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

首先,BZOJ2749: [HAOI2012]外星人

题中给出了这样的公式,然后我们发现每次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)

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int m=;
int p[m],f[m],n,t,i,j,x,y;
long long ans;
int main()
{
for(i=;i<=m;i++) p[i]=i;
for(i=;i<=m;i++)
if(p[i]==i)
for(j=i;j<=m;j+=i) p[j]=p[j]/i*(i-);
p[]=,f[]=-;
for(i=;i<=m;i++) f[i]=f[p[i]]+;
f[]++,f[]++;
cin>>t;
while(t--)
{
scanf("%d",&n);
for(ans=,i=;i<=n;i++)
{
scanf("%d%d",&x,&y);
if(x==) ans--;
ans+=(long long)f[x]*y;
}
printf("%lld\n",ans);
}
return ;
}

代码:求2的个数

 #include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<string>
#define inf 1000000000
#define maxn 100000+5
#define maxm 500+100
#define eps 1e-10
#define ll long long
#define pa pair<int,int>
#define for0(i,n) for(int i=0;i<=(n);i++)
#define for1(i,n) for(int i=1;i<=(n);i++)
#define for2(i,x,y) for(int i=(x);i<=(y);i++)
#define for3(i,x,y) for(int i=(x);i>=(y);i--)
#define mod 1000000007
using namespace std;
inline int read()
{
int x=;char ch=getchar();
while(ch<''||ch>''){ch=getchar();}
while(ch>=''&&ch<=''){x=*x+ch-'';ch=getchar();}
return x;
}
int tot,p[maxn],f[maxn];
bool v[maxn];
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
f[]=;
for2(i,,maxn)
{
if(!v[i]){p[++tot]=i;f[i]=f[i-];}
for1(j,tot)
{
int t=i*p[j];
if(t>maxn)break;
v[t]=;
f[t]=f[i]+f[p[j]];
if(i%p[j]==)break;
}
}
int m=read();
while(m--)
{
int n=read();ll ans=;
for1(i,n)
{
int x=read(),y=read();
if(x==)ans--;
ans+=(ll)f[x]*y;
}
printf("%lld\n",ans);
}
return ;
}

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