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 源码。

京公网安备 11010502036488号