LightOJ1038 Race to 1 Again

题意

\(T\in[1,10^4]\)组数据,每组给定一个数字\(n\in[1,10^5]\)\(n\)每次除以\(n\)的一个因子,商记为新的\(n\)。求\(n\)被除到\(1\)的期望次数,答案允许\(10^{-6}\)以内的误差。

解法

预处理出\([1,10^5]\)以内的所有\(n\)的期望,离线查询。
对于每个数\(n\),可以累加其因子的期望次数,然后除去\(n\)的因子数再\(+1\),记为\(n\)的期望次数。

原理

显然选中任何一个因子是等概率事件。
\(x\)被除到\(1\)的期望次数为\(E(x)\)\(n\)的因子为\(a_i\),选中\(a_i\)的概率为为\(P(A_i)\),则$$E(n)=\sum\limits_{a_i}{[E(a_i)+1]P(A_i)}$$。若记\(n\)的因子数为\(fac\),则由上式可得

\[E(n)=\frac{\sum\limits^{fac}[E(a_i)+1]}{fac}=\frac{\sum\limits^{fac}E(a_i)}{fac}+1 \]

。若因子按大小排序,则最后一个因子必定是自身,即

\[E(n)=\frac{\sum\limits^{fac-1}E(a_i)+E(n)}{fac}+1 \]

,移项得

\[E(n)=\frac{\sum\limits^{fac-1}E(a_i)+fac}{fac-1} \]

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define reg register int
using namespace std;
const int LIM = 1e5;
double dp[LIM + 1];
int main(){
	for(reg i = 2; i <= LIM; ++ i){
		double num = 0, ans = 0;
		for(reg j = 1; j * j <= i; ++ j){
			if(i % j == 0){
				++ num;
				ans += dp[j];
				if(j != i / j){ // 此处由于dp[i]还未计算,所以不特判i / j == i不影响答案。
					++ num;
					ans += dp[i / j]; // 同理 
				}
			}
			dp[i] = (ans + num) / (num - 1); // 经化简的公式
		}
	}
	int T, Case = 0; scanf("%d", & T);
	while(T --){
		int n;
		scanf("%d", & n);
		printf("Case %d: %.6f\n", ++ Case, dp[n]);
	}
	return 0;
}
posted @ 2019-06-04 16:57  Divinitist  阅读(116)  评论(0)    收藏  举报