【bzoj 2749】: [HAOI2012]外星人


http://www.lydsy.com/JudgeOnline/problem.php?id=2749


妈蛋官方数据是错的,害我查半天。。。。

留下个草稿或者说在线思路。。。。


my thoughts:


f(x)=0 (x=1)
f(x)=f(phi(x))+1 (else)


那么显然是可以O(n)求出来的


令k=i*prim[j]
f(1)=0
f(i)=f(i-1)+1 (i is a prime)
f(k)=f(phi(k))+1=f(phi(i)*prim[j])+1 (i%prim[j]==0)
f(k)=f(phi(k))+1=f(phi(i)*prim[j]-phi(i))+1 (else)
。。。。到此没啥思路


看看下面的提示
phi(p^q)=(p-1)*p^(q-1)


设x>2
则phi(x)必为偶数
每次变换一次必然消掉一些素因子,产生2
所以最后肯定是累积了大量的2


设g(x)为分解的过程中产生2的个数。。。


g(1)=0
g(2)=1


假设x为奇数
f(x)=g(x)+1


假设x为偶数
f(x)=g(x)


问题转化为如何求g(x)
g(1)=0
g(2)=1
g(2^q)=q
for p>2:
g(p)=g(p-1) (i is a prime)
g(p^q)=q*g(p-1)
g(p1p2)=g(p1)+g(p2) (phi是积性函数)


所以只需要考虑x<=10^5
发现最开始的时候我们已经解决了f(x),那么可以直接求g(x)


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
#define rep(i,l,r) for(int i=(l),_=(r);i<=_;i++)
#define per(i,r,l) for(int i=(r),_=(l);i>=_;i--)
#define MS(arr,x) memset(arr,x,sizeof(arr))
#define INE(i,u) for(int i=head[u];~i;i=e[i].next)
#define LL long long
inline const int read()
{int r=0,k=1;char c=getchar();for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';return k*r;}

const int N=100010;
int prim[N],f[N],phi[N],cnt;
bool flag[N];
int g[N];

void init()
{
	f[1]=0;
	rep(i,2,100000)
	{
		if(!flag[i]) prim[++cnt]=i,phi[i]=i-1,f[i]=f[i-1]+1;
		for(int j=1;i*prim[j]<=100000&&j<=cnt;j++)
		{
			int k=i*prim[j];
			flag[k]=1;
			if(i%prim[j]==0)
			{
				phi[k]=phi[i]*prim[j];
				break;
			}
			phi[k]=phi[i]*(prim[j]-1);
		}
	}
	rep(i,2,100000) f[i]=f[phi[i]]+1;
	
	g[1]=0;
	g[2]=1;
	rep(i,3,100000)
	{
		if(i&1) g[i]=f[i]-1;
		else g[i]=f[i];
	}
}

void input()
{
    
}
void solve()
{
	init();
    rep(i,1,read())
    {
    	int n=read();
    	LL ans=0;
    	bool flag=0; //是否为偶数 
    	rep(i,1,n)
    	{
    		int p=read(),q=read();
    		if(p==1) continue;
    		if(p==2)
    		{
    			flag=1;
    			ans+=q;
    		}
    		else
    		{
    			ans+=(LL)q*g[p-1];
    		}
    	}
    	if(!flag) ans++;
    	printf("%lld\n",ans);
    }
}

int main()
{
    freopen("_.in","r",stdin); freopen("_.out","w",stdout);
    input(),solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值