首页 > 其他分享 >P9007 [入门赛 #9] 最澄澈的空与海 (Hard Version) 题解

P9007 [入门赛 #9] 最澄澈的空与海 (Hard Version) 题解

时间:2024-01-13 17:34:30浏览次数:31  
标签:P9007 cnt frac int 题解 Hard times num dp

Upd on 2023.10.14 08:21:修改了推式子和题意的一些小错误。

前言

一道恐怖的绿题。显然我认为应该是蓝题。(不过在这篇题解写到一半的时候升蓝了,感谢 @StudyingFather。)

名字挺好的。

题意

给定 \(n\),求出满足以下条件的三元组 \((x, y, z)\) 的组数:

  1. \(x \ge 0, z \ge 1\)。

  2. \(x - \frac{y}{z} = n!\) 且 \(\frac{x - y}{z} = \frac{n!}{n}\)。

  3. \(z \mid x,z \mid y\)。

答案为 \(\infty\) 时,输出 inf

多组数据。

解法

首先我们需要将式子变形。最开始,将两式相减,得到:

\[\begin{aligned} x - \frac{y}{z} - \frac{x - y}{z} = n! - \frac{n!}{n} \end{aligned}\]

然后继续化简:

\[\begin{aligned} x - \frac{y + (x - y)}{z} &= n! - (n - 1)! \\ x - \frac{x}{z} &= n \times (n - 1)! - (n - 1)!\\ \frac{x \times (z - 1)}{z} &= (n - 1) \times (n - 1)!\\ x &= \frac{(n - 1) \times (n - 1)! \times z}{z - 1} \end{aligned}\]

可以发现 \(y\) 被消掉了,于是不再需要考虑 \(y\)。

考虑 \(z > 1\) 的情况,\(x\) 要为整数,那么 \((n - 1) \times (n - 1)! \times z \mid z - 1\),而在 \(z \ne 2\) 时,显然 \(z \nmid z - 1\),那么就需要 \((n - 1) \times (n - 1)! \mid z - 1\)。那么这样的 \(x\) 的个数显然就是 \((n - 1) \times (n - 1)!\) 的约数个数。

于是这道题就转化为了求 \((n - 1) \times (n - 1)!\) 的约数个数。

根据约数个数定理和唯一分解定理,设 \(x = p_1^{q_1} \times p_2^{q_2} \times \dots p_k^{q_k}\),其中 \(p_i\) 为质数,那么 \(x\) 的约数个数为 \(\prod\limits_{i = 1}^{k}{(q_i + 1)}\)。

设 \(dp_i\) 表示 \(i\) 这个质数在 \((n - 1) \times (n - 1)!\) 出现的次数,那么每次的答案即为 \(\prod\limits_{i = 1}^{cnt}{(dp_i + 1)}\)。

不过显然这样质因数分解很慢,考虑优化。

首先考虑线性筛的本质。在线性筛的过程中,每个合数都只会被它最小的质因数标记。考虑把它存下来,这样质因数分解的复杂度就降到了 \(\Theta(\log{n})\)。由于是阶乘,显然需要从 \(1\) 到 \(n - 1\) 枚举,每次都质因数分解存入 \(dp_i\) 中,不然 \(10^6!\),可想而知。这样的总时间复杂度为 \(\Theta(n \log{n})\) 的,但是如果再加上多测,复杂度就变为了 \(\Theta(T n \log{n})\),显然是不行的。

考虑继续优化上面的过程。

由于增加了多测,那么我们可以换一种方式,不一定要在线求答案,考虑预处理。

设 \(Ans_i\) 表示 \(i \times i!\) 的答案。将每一个 \(i\) 进行质因数分解,设 \(p\) 为当前分解到的质因数,\(num\) 为这个质因数出现的次数。那么 \(dp_p + num \times 2 \to dp_p\)。这里 \(dp_p\) 乘 \(2\) 的原因是还有一个 \(i\) 要乘。不过根据 \(dp\) 的定义,乘 \(i\) 的答案不能算进来,所以之后将答案存进 \(Ans_i\) 后,还要减去一个 \(num\)。然后将 \(\prod\limits_{i = 1}^{cnt}{(dp_i + 1)}\) 存入 \(Ans_i\)。这时我们会发现处理出 \(\prod\limits_{i = 1}^{cnt}{(dp_i + 1)}\) 是一个很慢的过程。考虑优化这个过程。

设 \(ans\) 为当前 \(\prod\limits_{i = 1}^{cnt}{(dp_i + 1)}\) 的值。对于每一个 \(i\),将 \(i\) 进行质因数分解。设 \(p\) 为当前分解到的质因数,\(num\) 为这个质因数出现的次数。那么此时的 \(dp_p\) 比上一个 \(dp_p\) 增加了 \(num\),将 \(ans \div dp_p\),再将 \(dp_p + num \times 2\),最后将 \(ans \times dp_p\) 即可。根据前文所说,\(dp_p\) 还需要减去一个 \(num\),那么在 \(ans\) 存入 \(Ans_i\) 后,将 \(ans \div dp_p\),\(dp_p - num\),再将 \(ans \times dp_p\) 即可。

由于 \(ans\) 需要取模,所以这里的除法全部改为乘逆元即可。

最后处理一个特殊情况,前文我们都只考虑了 \(z > 1\) 的情况,当 \(z = 1\) 时,情况就会变成这样:

\[\begin{cases} x - y = n! \\ x - y = \frac{n!}{n} \end{cases}\]

那么 \(n! = \frac{n!}{n}\),显然 \(n = 1\)。

那么这时候,\(x - y = 1\),符合条件的三元组显然有 \(\infty\) 个,输出 inf 即可。

毒瘤。

AC Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5, Mod = 998244353;
int t, n, Ans[N], dp[N], pri[N], fac[N], inv[N];
vector<int> prime;
vector< pair<int, int> > f;
bool vis[N];
void Prime(int x)
{
    vis[1] = true;
    for(int i = 2; i <= x; i++)
    {
        if(!vis[i])
            prime.push_back(i), pri[i] = i;
        for(auto p : prime)
        {
            if(i * p > x)
                break;
            vis[i * p] = true;
            pri[i * p] = p;
            if(!(i % p))
                break;
        }
    }
    return;
}
void p_fac(int x)
{
    f.clear();
    while(x != 1)
    {
        int y = pri[x], cnt = 0;
        while(!(x % y))
            x /= y, cnt++;
        f.push_back(make_pair(y, cnt));
    }
    return;
}
void Inv(int x)
{
    inv[1] = 1;
    for(int i = 2; i <= x; i++)
        inv[i] = (Mod - Mod / i) * inv[Mod % i] % Mod;
    return;
}
void init(int n)
{
    Prime(1e6);
    Inv(1e6);
    int ans = Ans[0] = 1;
    for(int i = 1; i <= n; i++)
    {
        p_fac(i);
        for(auto j : f)
        {
            int p = j.first, num = j.second;
            ans = ans * inv[dp[p] + 1] % Mod;
            dp[p] += num * 2;
            ans = ans * (dp[p] + 1) % Mod;
        }
        Ans[i] = ans;
        for(auto j : f)
        {
            int p = j.first, num = j.second;
            // cout << ans << " " << inv[dp[p] + 1] << " " << dp[p] + 1 << "    ";
            ans = ans * inv[dp[p] + 1] % Mod;
            dp[p] -= num;
            ans = ans * (dp[p] + 1) % Mod;
        }
    }
    return;
}
signed main()
{
    init(1e6);
    cin >> t;
    while(t--)
    {
        cin >> n;
        cout << (n == 1 ? "inf" : to_string(Ans[n - 1])) << "\n";
    }
    return 0;
}

标签:P9007,cnt,frac,int,题解,Hard,times,num,dp
From: https://www.cnblogs.com/Luckies/p/17962648/P9007_Solution

相关文章

  • AT_abc243_g [ABC243G] Sqrt题解
    题目大意有一个数列,初始时只有一个数\(X\)。你可以对它进行一种操作:设末尾的数为\(Y\),从\(1\sim\sqrt{Y}\)中选一个数加到数列的末尾。如此进行\(10^{100}\)次操作,问数列一共有多少种可能的状态。解法考虑DP。设\(dp_i\)表示以数字\(i\)开头的数列可能的状态。设......
  • CF713D 题解
    题意给一个\(01\)矩阵,多次求在给定区间内最大的全\(1\)正方形边长。思路容易想到二分:先预处理出以每个位置为右下角的最大合法正方形的边长\(mx_{i,j}\),然后对于每个询问,我们二分边长\(mid\),设当前询问的区间左上角为\((x_1,y_1)\),右下角为\((x_2,y_2)\),则能取到\(mi......
  • AT_arc167_e 题解
    题意给定\(k\)和一个排列\(P'\),问有多少个排列\(P\)以最少步数交换相邻两个元素来进行收敛,最终的排列可能是\(P'\),一个排列是收敛的当且仅当对于每一个数,在该数前且比这个数大的数的个数不超过\(k\)个。思路考虑正向的让一个排列收敛,我们设在第\(i\)个位置前且比\(P......
  • AT_agc054_c 题解
    题意给定\(k\)和一个排列\(P'\),问有多少个排列\(P\)以最少步数交换相邻两个元素来进行收敛,最终的排列可能是\(P'\),一个排列是收敛的当且仅当对于每一个数,在该数前且比这个数大的数的个数不超过\(k\)个。思路考虑正向的让一个排列收敛,我们设在第\(i\)个位置前且比\(P......
  • P9754 题解
    题意不难理解,不多赘述。思路首先考虑对于性质A的情况,我们可以这样做:定义一个代表变量的结构体,里面存几个参数:首先肯定要存种类(\(type\))和名称(\(name\)),其次为了方便,我们把该变量的大小(\(siz\)),起始位置(\(fir\))和对齐要求(\(mx\))也存了。操作二\(type\),\(name\),\(siz\)和\(m......
  • AT_cf17_final_j 题解
    题意给定一棵既有点权也有边权的树,构造一个完全图,图中两点间边的边权为树中两点点权之和加上两点间的距离,求该图的最小生成树。思路发现完全图总边数太大,考虑减少边数。这里有一个性质:如果在一个图中选取任意个联通的边集,使得它们的并为全集,则整个图的最小生成树中的边一定在......
  • [AGC022F] Checkers 题解
    题目链接点击打开链接题目解法很妙的题!!!考虑\(x\)是无穷大的数,所以可以认为\(x^i\)的系数是单独的一项,不会和\(x^j(j\neqi)\)合并所以问题转化成了:每个数初始是\(x^i\)(\(x\)可以理解是元),进行题目中的操作,问最后形成的\(n\)次多项式的个数由\(B\)向\(A\)连边,这......
  • CF1006E Military Problem 题解
    CF1006EMilitaryProblem题解题意给定一颗有\(n\thinspace(2\leqn\leq2\times10^5)\)个节点的树,树根为\(1\)。对于每个节点\(i\thinspace(2\leqi\leqn)\)都有它的父节点\(p_i\),并且每个节点的子节点都是按从小到大的顺序排列的的。有\(q\thinspace(1......
  • 【题解】CatOJ C0458C 滑动窗口定期重构
    标题trick的名字我也不知道是什么,就这样吧。link。首先有显然的dp式子:\(f(i)=\min\{f(j)\times\max\{a_{j+1},\dots,a_i\}\}\)。考虑怎么去优化它。有显然的\(\mathcalO(n\logn)\):考虑线段树优化dp。用增的单调栈维护\(a\),若每次弹出顶部一个下标\(p\),则\([p+1,i......
  • 1.11模拟赛 T1题解
    简要题意\(n\le10^3,\sumK_i\le3\times10^5\)思路首先容易想到一个暴力DP,\(f_{l,r,x}\)表示区间中最大值为\(x\)的最大值稍微想亿下可以发现如果这个位置选的不是区间最大值的话,答案一定不优所以我们可以直接\(f_{l,r}\)DP转移,但复杂度还是没变,我们把柿子列出来\(......