这个板子是真(TM)毒。心脑血管疾病患者慎入
传送门
总之先来康柿子,给定非负整数(n,a,b,c),求下面三个柿子(这里的(g,h)顺序好像和题目颠倒了......问题不大):
[f(a,b,c,n) = sumlimits_{i = 0}^{n} leftlfloor frac{ai + b}{c}
ight
floor
]
[g(a,b,c,n) = sumlimits_{i = 0}^{n} i leftlfloor frac{ai + b}{c}
ight
floor
]
[h(a,b,c,n) = sumlimits_{i = 0}^{n} leftlfloor frac{ai + b}{c}
ight
floor ^ 2
]
并输出对(998244353)取模的结果,保证(0 le n,a,b,c le 10^9, c
ot= 0)。
(f(a,b,c,n)):
由于(a)非负,所以(leftlfloor frac{ai + b}{c}
ight
floor)对于(i)是递增的,为了方便,下面令(d(i) = leftlfloor frac{ai + b}{c}
ight
floor)。
换句话说,(d(i))的最大值就是(d(n)),我们令(m = d(n))。
那么就有
[d(i) = sumlimits_{j = 1}^{m} left[ j le d(i)
ight]
]
令(j = j-1)(为什么呢?等会就知道了。)
[d(i) = sumlimits_{j = 0}^{m - 1} left[ j+1 le d(i)
ight]
]
然后把取整符号去掉
[d(i) = sumlimits_{j = 0} ^ {m-1} left[ j+1 le frac{ai + b}{c}
ight]
]
我们还能进一步化简中括号里的柿子
[j + 1 le frac{ai + b}{c} Leftrightarrow cj + c - b le ai
]
这里直接两边除以(a)?然而并不能。因为除以(a)后我们肯定会再把取整符号填上去,但(frac{cj + c - b}{a} le i)并不能说明(leftlfloor frac{cj + c - b}{a}
ight
floor le i)。
所以我们还是要把两边减一再变
[cj + c - b - 1 < ai Leftrightarrow frac{cj + c - b - 1}{a} < i Leftrightarrow leftlfloor frac{cj + c - b - 1}{a}
ight
floor < i
]
再带回(d(i)),同时令(alpha = alpha(j) = leftlfloor frac{cj + c - b - 1}{a}
ight
floor)
[d(i) = sumlimits_{j = 0}^{m - 1} left[ i > alpha
ight]
]
回到原式
[egin{aligned}
f(a,b,c,n)
&= sumlimits_{i = 0}^{n} d(i) \
&= sumlimits_{i = 0}^{n} sumlimits_{j = 0}^{m - 1} left[ i > alpha
ight]
end{aligned}
]
交换求和顺序再来颓柿子
[egin{aligned}
f(a,b,c,n)
&= sumlimits_{j = 0} ^ {m - 1} sumlimits_{i = 0} ^ {n} [i > alpha] \
&= sumlimits_{j = 0}^{m - 1} { (n+1) - (alpha + 1) } \
&= sumlimits_{j = 0} ^ {m - 1} { n - alpha} \
&= nm - sumlimits_{j = 0}^{m - 1} alpha
end{aligned}
]
后面的(sumlimits_{j = 0}^{m - 1} alpha)也就是(f(c, c -b - 1, a, m - 1)),于是(上面让(j)从(0)开始就是为了这一步)
[f(a,b,c,n) = nm - f(c,c-b-1,a,m-1)
]
没了?并没有......
发现如果(c - b - 1 < 0)的话这个柿子就炸了......
考虑让(b)对(c)取模,顺便也把(a)带走((a = 0)也是边界)。
即
[egin{aligned}d(i) &= leftlfloor frac{ai + b}{c}
ight
floor \&= leftlfloor frac{(leftlfloor frac{a}{c}
ight
floor c + a mod c)i + leftlfloor frac{b}{c}
ight
floor c + b mod c}{c}
ight
floorend{aligned}
]
注意到(leftlfloor frac{a}{c}
ight
floor c i)和(leftlfloor frac{b}{c}
ight
floor c)都是整数,且有约数(c),直接提出来
[d(i) = leftlfloor frac{a}{c}
ight
floor i + leftlfloor frac{b}{c}
ight
floor + leftlfloor frac{(a mod c) i + b mod c}{c}
ight
floor
]
于是
[f(a,b,c,n) = frac{n(n+1)}{2}leftlfloor frac{a}{c}
ight
floor + (n+1)leftlfloor frac{b}{c}
ight
floor + f(a mod c, b mod c, c, n)
]
这样就可以递归算了,边界就是(a = 0)
[f(a,b,c,n) = egin{cases}nm - f(c, c-b-1, a, m - 1) quad (a < c and b < c) \frac{n(n+1)}{2}leftlfloor frac{a}{c}
ight
floor + (n+1)leftlfloor frac{b}{c}
ight
floor + f(a mod c, b mod c, c, n) quad (a ge c or b ge c) \(n+1) leftlfloor frac{b}{c}
ight
floor quad (a = 0)end{cases}
]
时间复杂度:
观察(a)位置上和(c)位置上的数,第一次是((a,c))(不放设(a < c)),第(2)次会变为((c,a)),第三次((c mod a, a))然后再交换位置,再取模。这不就是(gcd)吗?于是复杂度就是(gcd)的复杂度(*2)。
(g(a,b,c,n))
类似上面的(f),我们也照这个样子推导(g)。(一些定义和上面类似)。
当(a ge c or b ge c)时
[egin{aligned}g(a,b,c,n) &= sumlimits_{i = 0}^{n} i cdot d(i)end{aligned}
]
把(d(i))展开
[egin{aligned}g(a,b,c,n)&= sumlimits_{i = 0} ^ n i cdot (leftlfloor frac{a}{c}
ight
floor i + leftlfloor frac{b}{c}
ight
floor + leftlfloor frac{(a mod c)i + b mod c}{c}
ight
floor) \&= sumlimits_{i = 0} ^ n leftlfloor frac{a}{c}
ight
floor i^2 + leftlfloor frac{b}{c}
ight
floor i + i cdot leftlfloor frac{(a mod c)i + b mod c}{c}
ight
floor \&= frac{1}{6}n(n+1)(2n+1)leftlfloor frac{a}{c}
ight
floor + frac{1}{2}n(n+1)leftlfloor frac{b}{c}
ight
floor + g(a mod c, b mod c, c, n)end{aligned}
]
当(a < c and b < c)时
[egin{aligned}g(a,b,c,n)&= sumlimits_{i = 0}^{n} i cdot d(i) \&= sumlimits_{i = 0} ^ n i sumlimits_{j = 0}^{m - 1}[i > alpha] \&= sumlimits_{j = 0} ^ {m-1} sumlimits_{i = 0}^{n} i cdot [i > alpha]end{aligned}
]
第二个(sum)就是对(alpha+1...n)这些整数求和,直接套公式
[egin{aligned}g(a,b,c,n)&= sumlimits_{j = 0}^{m - 1} frac{1}{2}(n-alpha)(n+alpha+1) \&= frac{1}{2} sumlimits_{j = 0}^{m - 1} n^2 + nalpha + n - nalpha - alpha^2 - alpha \&= frac{1}{2}left[ mn(n+1) - h(c, c-b-1, a, m-1) - f(c, c-b-1, a,m-1)
ight]end{aligned}
]
这里用到了(f)和(h)。
边界就是(a = 0)时,(g = frac{1}{2}n(n+1)leftlfloor frac{b}{c}
ight
floor)
发现(g)也是一个类似于(f)的递归过程,可以和(f)放到一起算。
(h(a,b,c,n))
感觉平方项很难通过枚举值算贡献的方法化简......
怎么办呢?
考虑到(1+2+...+n = frac{1}{2}n(n+1)),那么(n^2 = 2left(sumlimits_{k = 0}^{n}k
ight) - n)
带到(d(i))里再带回原柿就是
[egin{aligned}h(a,b,c,n)&= sumlimits_{i = 0}^{n} d(i)^2 \&= sumlimits_{i = 0}^{n} 2 left( sumlimits_{k = 0} ^ {d(i)} k
ight)-d(i) \&= left( 2 sumlimits_{i = 0} ^ n sumlimits_{k = 0} ^ {d(i)}k
ight) - f(a,b,c,n)end{aligned}
]
考虑怎么求(sumlimits_{i = 0} ^ n sumlimits_{k = 0} ^ {d(i)}k),同样的,让(j)代替(k),并从(0)跑到(m-1),然后交换求和顺序
[sumlimits_{j = 0} ^ {m - 1} sumlimits_{i = 0} ^ {n} [j +1 le d(i)] cdot (j+1)
]
中括号里我们已经化简过了,再把(j+1)提到外面
[egin{aligned}sumlimits_{j =0}^{m-1} (j+1) sumlimits_{i = 0}^{n} [i>alpha]&= sumlimits_{j = 0}^{m-1}(j+1)(n-alpha) \&= sumlimits_{j = 0}^{m - 1} n(j+1) - j cdot alpha - alpha \&= frac{1}{2} m(m+1)n - g(c, c-b-1, a, m-1) - f(c, c-b-1, a, m-1)end{aligned}
]
所以(h(a,b,c,n) = m(m+1)n - 2g(c,c-b-1,a,m-1) - 2f(c,c-b-1,a,m-1) - f(a,b,c,n))
取模的话暴力三元完平展开就好了
[h(a,b,c,n) = h(a mod c, b mod c, c, n) + 2 leftlfloor frac{b}{c}
ight
floor f(a mod c, b mod c, c, n) + 2leftlfloor frac{a}{c}
ight
floor g(a mod c, b mod c, c, n) + frac{1}{6}n(n+1)(2n+1) leftlfloor frac{a}{c}
ight
floor ^ 2 + (n+1) leftlfloor frac{b}{c}
ight
floor^2 + n(n+1) leftlfloor frac{a}{c}
ight
floor leftlfloor frac{b}{c}
ight
floor
]
边界就是(a = 0),(h(a,b,c,n) = leftlfloor frac{b}{c}
ight
floor ^ 2(n+1))。(h)也可以和(f,g)放到一起算。
总复杂度是(gcd)的复杂度。
(Code):
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
char ch; ll x = 0; int f = 1; while (!isdigit(ch = getchar())) f = ch=='-' ? -f : f;
while (isdigit(ch)){ x = x*10 + ch - '0'; ch = getchar(); } return x * f;
}
const int P = 998244353, i2 = 499122177, i6 = 166374059;
void update(ll &x, ll y) {
x += y; if (x >= P) x -= P;
}
inline ll s1(ll n) {
n %= P; return n % P * (n+1) % P * i2 % P;
}
inline ll s2(ll n) {
n %= P; return n % P * (n+1) % P * (2ll*n%P + 1) % P * i6 % P;
}
inline ll sqr(ll x) {
return 1ll * x * x % P;
}
#define chk(x) assert(0<=(x) && (x)<P)
void euclid(ll a, ll b, ll c, ll n, ll &f, ll &g, ll &h) {
ll ac = a/c, bc = b/c, m = 1ll * (1ll*a*n + b) / c;
if (a == 0) {
f = 1ll * bc * (n + 1) % P, g = 1ll * bc * s1(n) % P;
h = 1ll * sqr(bc) * (n + 1) % P;
return;
}
ll f1, g1, h1;
if (a >= c || b >= c) {
euclid(a % c, b % c, c, n, f1, g1, h1);
f = g = h = 0;
update(f, 1ll * s1(n) * ac % P), update(f, 1ll * bc * (n+1) % P), update(f, f1);
update(g, 1ll * s2(n) * ac % P), update(g, 1ll * s1(n) * bc % P), update(g, g1);
update(h, 1ll * s2(n) * sqr(ac) % P), update(h, 1ll * (n+1) * sqr(bc) % P);
update(h, h1), update(h, 2ll * ac * g1 % P);
update(h, 1ll * n * (n+1) % P * ac % P * bc % P);
update(h, 2ll * bc * f1 % P);
return;
}
euclid(c, c-b-1, a, m-1, f1, g1, h1);
f = g = h = 0;
update(f, 1ll * n * m % P), update(f, P - 1ll * f1 % P);
update(g, 1ll * n % P * (n+1) % P * m % P);
update(g, P - 1ll * f1 % P), update(g, P - 1ll * h1 % P), g = g * i2 % P;
update(h, 1ll * m * (m+1) % P * n % P), update(h, P - 2ll * g1 % P);
update(h, P - 2ll * f1 % P), update(h, P - f);
}
int main(){
for (int T = read(); T; T--) {
ll n = read(), a = read(), b = read(), c = read(), f, g, h;
euclid(a, b, c, n, f, g, h);
printf("%lld %lld %lld
", f, h, g);
}
return 0;
}
一般形式的话......估计这辈子都学不动了(QwQ)