[HAOI2012]外星人题解

一个好玩的题目
首先理解一下题意,就是说每次会给你一个数(被分解成其质因数乘积的形式),求对它用几次欧拉函数能使其变成(1)
例如样例 (N=2^2*3^1=12)
(12->4->2->1) 一共三次
又发现题目给了这玩意

[varphi(prod_{i = 1}^m p_i^{q_i}) = prod_{i = 1}^m (p_i - 1)*p_i^{q_i-1} ]

发现一个性质:每次操作就是将其除以其所有质因数之积,再乘上它们减一后的积
比如对于(N=60)时,一次操作就是((60/5/6/2)*4*5*1)
然后就发现,若(N)为合数,在每次操作中会(仅)使(N)中的一个(2)变为(1)
否则(N)会减一
于是我们记录每个数中有几个(2),比如说(5)(5->4->2*2)这么看有两个(2)
对于偶数,将其变为(1)的步数就是其(2)的个数
否则还要减一,就像上面(5)的例子一样
那么这个规律怎么和题目联系起来呢
因为每次最多使一个(2)变为(1),但是一个数"产出"(2)的回合数最多是(2)(还是看(5)的例子,(5)只需要一回合就能变成(2)的倍数)
所以一定会有很多多余的(2),那么我们就发现了,(N)变唯一的回合数就是把(2)都给搞定的回合数
(f)数组记录每个数能"生产"几个(2)

[f[a]=f[a-1](a为质数) ]

[f[a*b]=f[a]+f[b] ]

然后对于(N),我们只需要知道它的质因子们会提供几个(2),再按上面法则计算就好

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std;
const ll N=2e5;
ll n,m,f[N],a[N],top,T,ans;
bool pook[N],pool;

int main(){
	f[1]=1;
	for(ll i=2;i<=1e5;i++){
		if(!pook[i]){
			f[i]=f[i-1],a[++top]=i;
		}
		for(ll j=1;j<=top&&i*a[j]<=1e5;j++){
			pook[a[j]*i]=1,f[a[j]*i]=f[a[j]]+f[i];
			if(i%a[j]==0)break;
		}
	}
	scanf("%lld",&T);
	while(T--){
		scanf("%lld",&n);
		pool=0,ans=1;
		for(ll i=1,a1,a2;i<=n;i++){
			scanf("%lld%lld",&a1,&a2);
			ans+=f[a1]*a2;
			if(a1==2)pool=1;
		}
		ans-=(pool==1);
		printf("%lld
",ans);
	}
}
原文地址:https://www.cnblogs.com/caijiLYC/p/13970799.html