2020 多校第九场 E https://ac.nowcoder.com/acm/contest/5674/E

给定 ,求:

TL;DR:找找规律乱搞几下就出来了,WA 到怀疑人生就果断上


大家好,我又来丢人了。已经尽我所能地详细写了,希望 我公式没敲错 能帮到大家。

这里假定你能看懂题目的式子并把暴力做法给写出来。当然了暴力是不可能过的,但可以使用暴力模拟一下样例,寻找一些规律。

例如,在模拟样例二的时候,显而易见地 可以观察到:

  • 对于 ,只要 就一定成立;
  • 同理对于 ,只要 就一定成立;
  • 同理对于 ,不写了不写了自己算几项就有了。

可以猜想到可能会和 的因数有一定的联系,不妨就顺着这个思路探究一下这个恒成立的触发条件到底是什么,从而优化我们的暴力做法。

前置知识

  • 快速幂 - 就是用 的时间复杂度算出
  • 算术基本定理 - 任何一个大于 的自然数 ,都可以分解为有限个质数的乘积。
  • 利用算术基本定理计算两个数的最大公因数,这里随便找了一篇文章供参考

现在我们就利用算术基本定理计算 ,我们首先把 拆分成下面的形式:

其中,对于 ,有:

  • 是质数;

显然对于任意 ,根据算术基本定理,必定存在符合要求的写法。

那么 就可以写成下面的形式:

不难得到下面的式子:

如果看公式难以理解,这里举个栗子,假设要你计算

因为有:

所以有:

那么就可以算出结果:


现在我们可以把刚刚得出的式子代入到题目里:

然后做一些等价转换,目的是在式子中找到我们接下来需要关注的部分。

首先,根据乘法交换律:

实际上就是先枚举 ,然后再枚举

接下来,根据同底数幂相乘,底数不变指数相加:

我们在这里停一下,尝试口胡一下现在我们应该怎样计算这个式子:

首先对 进行质因数分解,并根据分解结果初始化 三个数组。要注意这些数组都不会再被更新了,也意味着对于每一个 都是确定的值。然后对于每一个 ,我们枚举 ,然后计算:

这一部分就是我们接下来要关注的部分,这是因为:

这是一个关于 的分段函数,而且其中一段是常函数,另一段是一次函数,这意味着我们可以往分段求和的方向想。

考虑临界值的位置:

第一种情况:,那么有:

第二种情况:,那么有:

第三种情况:,那么有:

如果还是对求和过程有疑问,其实这就是等差数列求和嘛!首项加末项乘以项数除以二。

当然在编程的时候可能希望把三种情况归类成一种情况,是可以的:

直接将边界调整到 范围内:

然后套用第二种情况的式子,你会发现将边界值调整后,这个式子同样适用于情况一和情况三。

复杂度足够应付这个数据范围了,那就到此为止。


接下来就是编程实现,刚才已经口胡过做法了(请看上面的粗体字)。有几个地方可能需要考虑的:

  • ,无论 是什么,很显然答案必定是 ,其实可以不特判的。
  • ,在计算边界值 的时候不考虑这个可能会有除零错误,但是很显然这种情况下 不会影响答案。
  • 数据范围,掐指一算可能会爆 ,那就使劲开
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 998244353;

ll a, b, c, d, x, y;
map<ll, pair<ll, ll>> fac;

ll qpow(ll a, __int128 b)
{
    ll ans = 1, base = a;
    while (b)
    {
        if (b & 1)
            ans = ans * base % MOD;
        base = base * base % MOD;
        b >>= 1;
    }
    return ans;
}

void update_factor(ll x, bool loc)
{
    ll temp_x = x;
    for (ll i = 2; i * i <= x; i++)
    {
        while (temp_x % i == 0)
        {
            loc == 0 ? fac[i].first++ : fac[i].second++;
            temp_x /= i;
        }
    }
    if (temp_x != 1)
    {
        loc == 0 ? fac[temp_x].first++ : fac[temp_x].second++;
    }
}

int main()
{
    cin >> a >> b >> c >> d >> x >> y;
    update_factor(x, 0);
    update_factor(y, 1);
    ll ans = 1;
    for (auto k : fac)
    {
        if (k.second.second == 0)
        {
            continue;
        }
        __int128 curcnt = 0;
        for (int i = a; i <= b; i++)
        {
            ll edge = k.second.first * i / k.second.second;
            edge = min(d, max(c - 1, edge));
            curcnt += (d - edge) * k.second.first * i;
            curcnt += k.second.second * (edge + c) * (edge - c + 1) / 2;
        }
        ans = ans * qpow(k.first, curcnt) % MOD;
    }
    cout << ans << endl;
}

大家一起加油鸭!可以随意转载本题解(注明来源),最后附上本文 Markdown 源码