题面
求 ,要求
级别的时间解决。
通常 。
洛谷模版链接:https://www.luogu.com.cn/problem/P5170
解析
(1)当 或
可以理解为 个
先单独处理,分别从每个
中拿出所有
,
也单独处理。
再将 个
剩下的和
加起来
。
(2)当 且
设 ,代表循环内下取整可以取到的最大整数。
上面这句话的意思:当 时,所有
的
都会被计入 1。
也就是说一个 的
,会被计入
次,求出的值和原式相同。
分析 Iverson 括号的内部条件,
这样我们就得到了和 有关的约束,原式变为:
将求和下标平移 ,将原式变成符合
函数的形式:
(1)当 或
(2)当 且
,
这两种变换使参数 变为
,与欧几里得算法的辗转相除结构一致,从而保证
的时间复杂度。
派生形式
函数的推导
(1)当 或
(2)当 且
设 ,代表循环内下取整可以取到的最大整数。
设 ,则原式为:
又因为:
所以,原式为:
函数的推导
(1)当 或
设 ,
,
,
,则有:
所以原式为:
(2)当 且
因为 ,则原式为:
,设
:
将求和下标平移 ,将原式变成符合
函数的形式:
代码
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL P = 998244353;
LL i2 = 499122177, i6 = 166374059; // 逆元
struct node {
LL f, g, h;
};
node dfs(LL a, LL b, LL c, LL n) {
if (n < 0) return {0, 0, 0};
node res;
LL qa = a / c, qb = b / c, ra = a % c, rb = b % c;
if (a == 0) {
res.f = (n + 1) * qb % P;
res.g = n * (n + 1) % P * i2 % P * qb % P;
res.h = (n + 1) * qb % P * qb % P;
}
else if (a >= c || b >= c){
node no = dfs(ra, rb, c, n);
res.f = (no.f + n * (n + 1) % P * i2 % P * qa % P + (n + 1) * qb % P) % P;
res.g = (no.g + n * (n + 1) % P * (2 * n + 1) % P * i6 % P * qa % P
+ n * (n + 1) % P * i2 % P * qb % P) % P;
res.h = (no.h + 2 * qa % P * no.g % P + 2 * qb % P * no.f % P +
n * (n + 1) % P * (2 * n + 1) % P * i6 % P * qa % P * qa % P +
n * (n + 1) % P * qa % P * qb % P + (n + 1) * qb % P * qb % P) % P;
}
else if (n == 0) {
res.f = qb;
res.g = 0;
res.h = qb * qb % P;
}
else {
LL m = (a * n + b) / c;
node no = dfs(c, c - b - 1, a, m - 1);
res.f = ((n * m % P - no.f) % P + P) % P;
LL term1 = m % P * n % P * (n + 1) % P * i2 % P;
LL term2 = (no.f + no.h) % P * i2 % P;
res.g = (term1 - term2 + P) % P;
res.h = (n % P * m % P * (m + 1) % P
- (2 * no.g) % P
- (2 * no.f) % P
- res.f + 4 * P) % P;
}
return res;
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T --) {
LL a, b, c, n;
cin >> n >> a >> b >> c;
node ans = dfs(a, b, c, n);
cout << ans.f << " " << ans.h << " " << ans.g << "\n";
}
return 0;
}