[USACO3.2] 阶乘问题

Luogu P1134 [USACO3.2] 阶乘问题

首先,可以看到:-这道题的标签是数学-
于是乎,

数学 启动!

首先,我想到了 找规律
以每十个数(即 \(1\) ~ \(10\) , \(11\) ~ \(20\) , \(\dots\) )为单位,可以发现,每十个数相乘后的个位数为 \(0\) , 显然是不符合题意的


然后,我又想到了,\(2 \times 5=10\)\(10\) 会影响结果末尾的 \(0\) 的个数,于是,我舍弃了 \(2,5,10\) 然后得到了每十个数(实际上是 \(7\) 个)相乘后末尾的数是 \(8\) (事实上这好像是错的,因为我实际上只算了 \(1\) ~ \(10\) ),然后对于每一组( \(10\) 个),显然答案是 \(8^{整组数} \times 剩余的几个数(排除 2,5,1)\)
显然,这是错的
于是乎,我写了暴力

#include <iostream>

using namespace std;

int main()
{
  int n;
  cin >> n;
  long long ans = 1;
  for (int i = 1; i <= n; ++i)
  {
    ans = ans * i;
    while (ans % 10 == 0)
      ans = ans / 10;
  }
  cout << ans % 10 << '\n';
  return 0;
}

image

长达五分钟的思考时间

image

于是乎

我想到了因为除了 \(10=2\times 5\) , \(2\) , \(5\) 以外,还有 \(25=5\times 5\) (有两个因子 \(5\) ),\(8=2^3\) , \(6=2\times 3\) (也有一个因子 \(2\)

接着,我想到了可以统计因子 \(2,5\) 的个数,最终答案就是所有数除去因子 \(2,5\) 后的乘积 \(\times\)(因子\(2\) 的个数 \(>\) 因子 \(5\) 的个数 \(?\) \(2^{因子 2 的个数-因子5的个数}\) \(:\) \(5^{因子5的个数-因子2的个数}\)
显然“ \(\times\) ” 后面的这个东西可以直接快速幂,

蛋柿 \(\dots\)

-又想到了-既然只有 \(2\)\(5\) , 何必要用快速幂呢,直接用数组存得了,于是乎,

#include <iostream>
#include <algorithm>

using namespace std;

int cnter, cntwu;
int er[(int)5e6 + 10];
int wu[(int)5e6 + 10];

int main()
{
  int n;
  cin >> n;
  long long ans = 1;
  er[0] = 1;
  for (int i = 1; i <= 5e6; ++i)
  {
    er[i] = er[i - 1] * 2 % 10;
  }
  wu[0] = 1;
  for (int i = 1; i <= 5e6; ++i)
  {
    wu[i] = wu[i - 1] * 5 % 10;
  }
  for (int i = 1; i <= n; ++i)
  {
    int k = i;
    while (k % 2 == 0 && k > 0)
    {
      k /= 2;
      cnter++;
    }
    // k = i;
    while (k % 5 == 0 && k > 0)
    {
      k /= 5;
      cntwu++;
    }
    cnter -= min(cnter, cntwu);
    cntwu -= min(cntwu, cnter);
    ans = ans * k % 10;
  }
  if (cnter == cntwu && cnter == 0)
  {
    cout << ans % 10 << '\n';
  }
  else if (cnter > 0)
  {
    cout << ans * er[cnter] % 10 << '\n';
  }
  else
  {
    cout << ans * wu[cntwu] % 10 << '\n';
  }
  return 0;
}

image

又是长达 \(10 \min\) 的调整

(我改了数组的大小为 \(2e7 +10\) , 全部 \(TLE\))
(甚至还改了数组为 \(short\) 类型 , 还是 \(RE\) 一个点 )
(然后,我想到:-既然数组会炸,那为何不用快速幂呢-)

于是乎

#include <iostream>
#include <algorithm>

using namespace std;

int cnter, cntwu;
long long ksm(int a, int b, int p)
{
  if (b == 0)
  {
    return 1 % p;
  }
  if (b == 1)
  {
    return a % p;
  }

  long long ans = ksm(a, b / 2, p);

  ans = 1ll * ans * ans % p;
  if (b % 2)
  {
    ans = 1ll * ans % p * a % p;
  }
  return ans;
}

int main()
{
  int n;
  cin >> n;
  long long ans = 1;
  for (int i = 1; i <= n; ++i)
  {
    int k = i;
    while (k % 2 == 0 && k > 0)
    {
      k /= 2;
      cnter++;
    }
    // k = i;
    while (k % 5 == 0 && k > 0)
    {
      k /= 5;
      cntwu++;
    }
    cnter -= min(cnter, cntwu);
    cntwu -= min(cntwu, cnter);
    ans = ans * (k % 10) % 10;
  }
  if (cnter == cntwu && cnter == 0)
  {
    cout << ans % 10 << '\n';
  }
  else if (cnter > 0)
  {
    cout << ans * ksm(2, cnter, 10) % 10 << '\n';
  }
  else
  {
    cout << ans * ksm(5, cntwu, 10) % 10 << '\n';
  }
  return 0;
}

image

总结,本文作者是 \(\color{red}SB\)

posted @ 2024-12-10 22:52  SigmaToT  阅读(19)  评论(0)    收藏  举报