📜  门|门 IT 2005 |第 67 题(1)

📅  最后修改于: 2023-12-03 15:28:48.026000             🧑  作者: Mango

题目简介

本题为 "门|门 IT 2005" 比赛的第 67 题。本题需要使用高精度计算,求出指定的数值问题的答案。

题目描述

给定两个正整数 $n, m$,其中 $n$ 为位数比较大的数,$m$ 为 1 或 2,根据 $m$ 的不同,求出以下两种数值问题的答案:

  1. $A=\sum_{i=1}^n \frac{1}{i}$,保留小数点后 $n$ 位数字。若小数点后第 $n+1$ 位数字为 $5$,则向下舍入。

  2. $B=\prod_{i=1}^n {\frac{(i^2+1)}{i^2}}$,保留小数点后 $n$ 位数字。若小数点后第 $n+1$ 位数字为 $5$,则向下舍入。

输入格式

输入文件的第一行包含两个正整数 $n$ 和 $m$,分别表示题目描述中给定的两个正整数。

输出格式

对于第一种数值问题,输出一个保留小数点后 $n$ 位的实数,保留答案小数位末尾的 $0$。若答案的 $n+1$ 位数字为 $5$,则向下舍入。

对于第二种数值问题,输出一个保留小数点后 $n$ 位的实数,保留答案小数位末尾的 $0$。若答案的 $n+1$ 位数字为 $5$,则向下舍入。

样例输入

5 1

样例输出

2.28333

解题思路

问题一

在第一种数值问题中,我们需要计算如下的数值:

$$ A=\sum_{i=1}^n \frac{1}{i} $$

直接计算这个式子是不可行的,因为对于较大的 $n$,它的精度会大大减小,计算的结果可能会失去一些准确性。考虑到这个式子的连续性,我们可以采用计算大量 $n$ 值时能成功的方式——从小到大累加相应的 $i$ 取倒数的值。在具体实现时,需要采用高精度技术,以保证计算过程中的精确性。

问题二

在第二种数值问题中,我们需要计算如下的数值:

$$ B=\prod_{i=1}^n {\frac{(i^2+1)}{i^2}} $$

类似于问题一,我们需要考虑到这个式子的连续性。事实上,这个问题可以转化为差分的形式:

$$ B=\prod_{i=1}^n {\frac{(i^2+1)}{i^2}} = \prod_{i=1}^{n-1} {\frac{(i^2+1)}{i^2}}\cdot {\frac{n^2+1}{n^2}} $$

就是说,在相邻因式之间,我们只需要计算它们的比值,也就是:

$$ {\frac{(i^2+1)}{i^2}}\div {\frac{((i-1)^2+1)}{(i-1)^2}}\ =\frac{(i^2+1)\cdot (i-1)^2}{i^2\cdot ((i-1)^2+1)} $$

转化成这个式子后,我们可以采用大整数的技术来求解。

参考实现

Python 3
import sys

def solve1(n):
    ans = 0
    for i in range(1, n+1):
        ans += (10**10000 * i + 5) // (10**10000 * i)
    return str(ans)[0:n//5] + '.' + str(ans)[n//5:n//5+5]

def solve2(n):
    ans = [1]
    for i in range(2, n+1):
        ans.append(ans[-1] * (10**10000 * i**2 + 1) // (10**10000 * i**2))
    res = str(ans[-1])

    left = 0
    right = len(res)
    while left < right - 1:
        mid = (left + right) // 2
        if res[mid] < '5':
            left = mid
        else:
            right = mid
    res = res[0:left] + res[left].replace(res[left], str(int(res[left])-1)) + '0'*(n-len(res[0:left])-1)
    return res[0:n//5] + '.' + res[n//5:n//5+5]

n, m = map(int, sys.stdin.readline().strip().split())
if m == 1:
    print(solve1(n))
else:
    print(solve2(n))
C++
#include <bits/stdc++.h>
using namespace std;
const int N = 10001;

struct Big
{
    int a[N], len;
    Big()
    {
        memset(a, 0, sizeof(a)), len = 0;
    }
    void read()
    {
        char s[N];
        scanf("%s", s+1);
        len = strlen(s+1);
        for (int i = 1; i <= len; i++) a[i] = s[len-i+1]-'0';
    }
    void print()
    {
        printf("%d", a[len]);
        for (int i = len-1; i >= 1; i--)
            printf("%04d", a[i]);
        printf("\n");
    }
    void operator = (int x)
    {
        memset(a, 0, sizeof(a));
        while (x)
        {
            a[++len] = x % 10000;
            x /= 10000;
        }
        if (len == 0) len = 1;
    }
    Big operator + (Big x)
    {
        Big c = Big(); c.len = max(len, x.len);
        for (int i = 1; i <= c.len; i++)
        {
            c.a[i] += a[i] + x.a[i];
            c.a[i+1] += c.a[i]/10000;
            c.a[i] %= 10000;
        }
        if (c.a[c.len+1] > 0) c.len++;
        return c;
    }
    Big operator - (Big x)
    {
        Big c = Big(); c.len = len;
        for (int i = 1; i <= c.len; i++)
        {
            c.a[i] += a[i] - x.a[i];
            if (c.a[i] < 0) c.a[i+1]--, c.a[i] += 10000;
        }
        while (c.len > 1 && !c.a[c.len]) c.len--;
        return c;
    }
    Big operator * (int x)
    {
        Big c = Big(); c.len = len;
        for (int i = 1; i <= c.len; i++)
        {
            c.a[i] += a[i] * x;
            c.a[i+1] += c.a[i]/10000;
            c.a[i] %= 10000;
        }
        while (c.a[c.len+1]) c.len++;
        while (c.len > 1 && !c.a[c.len]) c.len--;
        return c;
    }
    Big operator * (Big x)
    {
        Big c = Big();
        c.len = len + x.len -1;
        for (int i = 1; i <= len; i++)
            for (int j = 1; j <= x.len; j++)
            {
                c.a[i+j-1] += a[i] * x.a[j];
                c.a[i+j] += c.a[i+j-1]/10000;
                c.a[i+j-1] %= 10000;
            }
        while (c.a[c.len+1]) c.len++;
        while (c.len > 1 && !c.a[c.len]) c.len--;
        return c;
    }
    Big operator / (int x)
    {
        Big c;
        int r = 0;
        for (int i = len; i >= 1; i--)
        {
            r = r * 10000 + a[i];
            c.a[i] = r / x;
            r %= x;
        }
        c.len = len;
        while (c.len > 1 && !c.a[c.len]) c.len--;
        return c;
    }
};

int n, m;

int main()
{
    scanf("%d %d", &n, &m);
    if (m == 1)
    {
        Big ans, sum = Big();
        for (int i = 1; i <= n; i++)
            sum = sum + Big(i).div(i);
        for (int i = 0; i < sum.len; i++)
            if (sum.a[i+1] >= 5) {sum.a[i+1] = 0; sum.a[i]++;}
        while (sum.len > 1 && !sum.a[sum.len]) sum.len--;
        for (int i = sum.len; i > 0; i--)
        {
            if (i == sum.len) printf("%d.", sum.a[i]);
            else printf("%04d", sum.a[i]);
        }
        printf("\n");
    }
    else
    {
        Big ans, sum = Big(1);
        for (int i = 1; i <= n-1; i++)
            sum = sum * Big(i*i+1).div(i*i);
        sum = sum * Big(n*n+1).div(n*n), ans = sum;
        for (int i = 0; i < n+4; i++)
            if (ans.a[i+1] >= 5) {ans.a[i+1] = 0; ans.a[i]++;}
        while (ans.len > 1 && !ans.a[ans.len]) ans.len--;
        for (int i = ans.len; i > 0; i--)
        {
            if (i == ans.len) printf("%d.", ans.a[i]);
            else printf("%04d", ans.a[i]);
        }
        printf("\n");
    }
}