LibreOJ #138 类欧几里得算法
题意
T≤1000 组数据。给定 n,a,b,c∈[0,109],k1,k2∈[0,10],k1+k2≤10,求取
x=0∑nxk1⌊cax+b⌋k2
部分分——观察性质
当 k1=0,k2=1 时,是为一般类欧几里得算法的模板。上述链接亦给出了 k1=0,k2=2;k1=1,k2=1 时的解法。
当 k2=0 时,该式退化为 ∑x=0nxk1,即 k1 次的等幂求和。有一个模糊的结论:
设 n 为正整数。则k 次的等幂求和,∑x=0nxk,是一个关于 n 的 k+1 次多项式。
该结论来源于 zyw 学姐多项式专题所选题目 BZOJ 3453 – tyvj 1858 XLkxc。之所以说模糊,是因为该多项式的系数是已知的。不过我们仍然可以暴力计算 k+2 个点以插值。
再观察 OI-Wiki 上对于 k1+k2≤2 时的解法。在求取
h(n,a,b,c)=x=0∑n⌊cax+b⌋2时采取了如下转化:
x2=2×2x(x+1)−x=2(y=1∑xy)−x⟹x=0∑n⌊cax+b⌋2=x=0∑n2y=1∑⌊cax+b⌋y−⌊cax+b⌋
这样一来,由于 y 是一个线性算子,在 ax+b≥yc 时都有 y 向总和贡献,于是我们可以应用类似于 k1=0,k2=1 时的方法转化贡献。这个过程对 k2 作了降次。
那么,对于更高次项,有无办法采取同样的办法转化呢?是否可以设计一些转化,使得要求取的函数 f(⋯,k1,k2) 能够由 f(⋯,k1−?,k2−?) 推出呢?
B0…10={1,−21,61,0,−301,0,421,0,−301,0,665}
记 Sm(n)=x=0∑n−1xm,则有如下性质:
Sm(n)=m+11k=0∑m(km+1)Bknm+1−k
用红笔标注是因为第二版推导的“等幂求和”定义是 ∑x=1nxm(OI-Wiki 的转化——在下文中出现的那个——用的是这种)。最终检查时才发现不对劲。
(不过,将 B1 改为 21 的情况下,应用“错误”的“等幂求和”公式得到的结果是正确的,似乎也能计算。但这会牵扯到 x=0 时的计算 和 00=1 的定义问题,有点麻烦。暂且按下不表。)
我们移项可得
(m+1)Sm(n)nm+1nm=(0m+1)B0nm+1+k=1∑m(km+1)Bknm+1−k=(m+1)Sm(n)−k=1∑m(km+1)Bknm+1−k=mSm−1(n)−k=1∑m−1(km)Bknm−k
上式右侧是一个等幂求和以及若干 nk,k<m 与常数项之积的和。当 m>1 时,若能够快速处理 mSm−1(n) 这一部分的贡献,便能使用 k2 更小时的函数值计算。这便是处理 ⌊cax+b⌋m 的转化的一般形式。
推导过程
类似于一般类欧的思路,我们尽可能将 (a,b,c) 的情况化归为 (amodc,?,c) 再转化贡献。
推导 f
f(n,a,b,c,k1,k2)=x=0∑nxk1⌊cax+b⌋k2
记 α=⌊ca⌋,β=⌊cb⌋,a’=amodc,b’=bmodc。则
原式=x=0∑nxk1⌊c(αc+a’)x+(βc+b’)⌋k2=x=0∑nxk1⌊αx+β+ca’x+b’⌋k2=x=0∑nxk1(αx+β+⌊ca’x+b’⌋)k2=x=0∑nxk1e1+e2+e3=k2e1,e2,e3∈N∑(e1,e2,e3k1)(αx)e1βe2⌊ca’x+b’⌋e3=e1+e2+e3=k2e1,e2,e3∈N∑αe1βe2(e1,e2,e3k2)x=0∑nxk1+e1⌊ca’x+b’⌋e3(整数自由移出取整号)
(e1,e2,e3k2) 是多项式系数。有 (m1,m2,m3n)=m1!m2!m3!n!,是为 (a+b+c)n 展开式中 am1bm2cm3 的系数。
推导 f’
f′(n,a’,b’,c,k1,k2)=x=0∑nxk1⌊ca’x+b’⌋k2(a’,b’∈[0,c))
记 tx=⌊ca’x+b’⌋,则
原式=x=0∑nxk1(k2Sk2−1(tx)−q=1∑k2−1(qk2)Bqtxk2−q)=k2(x=0∑nxk1Sk2−1(tx))−q=1∑k2−1(qk2)Bqx=0∑nxk1txk2−q=k2(x=0∑nxk1Sk2−1(tx))−q=1∑k2−1(qk2)Bqf′(n,a’,b’,c,k1,k2−q)(等幂求和公式变形)
右侧可以由 k2′<k2 的函数值求出;现在考虑如何转化左侧的贡献。
推导 g’
g′(n,a’,b’,c,k1,k2)=x=0∑nxk1Sk2(⌊ca’x+b’⌋)=x=0∑nxk1y=0∑⌊ca’x+b’⌋−1yk2(a’,b’∈[0,c))
发现 Sm(n) 是前缀和的形式。则对于固定的 y,当
⟹⟹⌊ca’x+b’⌋−1≥y a’x+b≥(y+1)c x>⌊a’yc+c−b’−1⌋ 时,均有 yk2 对总和作出贡献。
记 p=⌊ca’n+b’⌋。则
原式=y=0∑p−1yk2x=⌊a’yc+c−b’−1⌋+1∑nxk1=y=0∑p−1yk2(Sk1(n+1)−Sk1(⌊a’yc+c−b’−1⌋+1))=Sk2(p)Sk1(n+1)−y=0∑p−1yk2Sk1(⌊a’yc+c+a’−b’−1⌋)
右侧看起来……很眼熟。它像是 g′(p−1,c,c+a’−b’−1,a’,k2,k1),但此时不可能有 c∈[0,a)。我们必须在这个唯一利用到下层递归函数值的地方做文章。
推导 g
g(n,a,b,c,k1,k2)=x=0∑nxk1Sk2(⌊cax+b⌋)=x=0∑nxk1q=0∑k2k2+11(qk2+1)Bq⌊cax+b⌋k2+1−q=q=0∑k2k2+11(qk2+1)Bqx=0∑nxk1⌊cax+b⌋k2+1−q(等幂求和公式)
右侧终于出现了我们所熟知的 f(n,a,b,c,k1,k2+1−q)。可以发现在同一层中 f 的计算仅仅依赖 g’ 而非 g,g’ 调用的是下层递归的 g,故我们已经完成了计算。
最终的递归式子
结合上文,我们可以立刻得到:
f(n,a,b,c,k1,k2)f′(n,a’,b’,c,k1,k2)g′(n,a’,b’,c,k1,k2)g(n,a,b,c,k1,k2)=e1+e2+e3=k2e1,e2,e3∈N∑αe1βe2(e1,e2,e3k2)f′(n,a’,b’,c,k1+e1,k2)=k2g′(n,a’,b’,c,k1,k2−1)−q=1∑k2−1(qk2)Bqf′(n,a’,b’,c,k1,k2−q)=Sk2(p)Sk1(n+1)−g(p−1,c,c+a’−b’−1,a’,k2,k1)=q=0∑k2k2+11(qk2+1)Bqf(n,a,b,c,k1,k2+1−q)(p=⌊ca’n+b’⌋)
边界条件有 n<0∨c=0 时返回 0;同时 f′(n,a,b,c,k1,k2=0)=Sk1(n+1),不需要(也不能——因为 S−1(n) 是未定义的)递归计算。
慢着。细心的你可能发现了一个问题:我们调用了 g′(⋯,k1,k2),g’ 调用了下一层的 g,而 g(⋯,k1,k2) 的计算式里要调用 f(⋯,k1,k2+1−q),q=0 的话岂不是造成了无限递归?!
稍加分析可得,在 f′(⋯,k1,k2) 中我们最多调用 g′(⋯,k1,k2−1),而其调用下一层的 g(⋯,k2−1,k1),此时套入上式得到其最多调用 f(⋯,k2−1,k1+1),在此过程中 两参数之和=k2+k1,保持不变。故而我们只需计算两参数之和 ≤(题面给定的)k1+k2=ksum 的所有函数值,它便是自洽的。
通过上述式子分析得到,单查询时间复杂度 O(logmax(a,c)(k1+k2)3)。通过预先计算三项式系数、伯努利数与二项式系数之积,以及同层内的 Sk(n),Sk(p),k∈[0,ksum],可以优化常数。
提交记录 #1604976
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define fst first
#define scd second
#define empb emplace_back
#define all(p) begin (p), end (p)
using ll = long long;
using ull = unsigned long long;
using pint = pair <int, int>;
constexpr int P = 1e9 + 7, G = 11;
constexpr ll B[] = { 1, 500000003, 166666668, 0, 766666672,
0, 23809524, 0, 766666672, 0, 348484851 }, PP = (ll) P*P,
fact[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800 },
finv[] = { 1, 1, 500000004, 166666668, 41666667, 808333339,
301388891, 900198419, 487524805, 831947206, 283194722 },
inv[] = { 0, 1, 500000004, 333333336, 250000002, 400000003, 166666668,
142857144, 125000001, 111111112, 700000005, 818181824 };
struct coeffi {
ll co[G+1][G+1];
constexpr coeffi () : co () {
for (int x = 0; x <= G; ++x) {
co[x][0] = 1;
for (int y = 1; y <= x; ++y)
co[x][y] = co[x-1][y] + co[x-1][y-1];
}
for (int x = 0; x <= G; ++x)
for (int y = 0; y < x; ++y)
(co[x][y] *= B[y]) %= P;
}
inl constexpr const ll* operator []
(int x) const { return co[x]; }
} constexpr co;
struct trinom {
ll c[G][G][G];
constexpr trinom () : c () {
for (int k = 0; k < G; ++k)
for (int x = 0; x <= k; ++x)
for (int y = 0; y + x <= k; ++y)
c[k][x][y] = fact[k] * finv[x] % P
* finv[y] % P * finv[k-x-y] % P;
}
inl constexpr const auto operator []
(int x) const { return c[x]; }
} constexpr tri;
int T, n, a, b, c, k1, k2, ksum;
ll f[G][G], f_[G][G], g[G][G], g_[G][G],
s_p[G], s_n1[G], _g[G][G];
inl ll S (int m, int n) {
static ll np, res; np = n, res = 0;
for (int k = m; k >= 0; --k, (np *= n) %= P)
res += np * co[m+1][k] % P;
return res % P * inv[m + 1] % P;
}
void calc (int n, ll a, ll b, ll c) {
if (n < 0 || !c) {
memset (f, 0, sizeof f),
memset (g, 0, sizeof g);
return; }
const ll alp = a / c, bta = b / c, _a = a % c,
_b = b % c, p = (n*_a + _b) / c;
static ll tmp, ap, bp;
calc (p - 1, c, c+_a-_b-1, _a);
for (int k = 0; k <= ksum; ++k)
s_p[k] = S (k, p), s_n1[k] = S (k, n + 1);
memcpy (_g, g, sizeof g);
for (int k1 = 0; k1 <= ksum; ++k1)
for (int k2 = 0; k2 + k1 <= ksum; ++k2)
g_[k1][k2] = (s_p[k2]*s_n1[k1] + P - _g[k2][k1]) % P;
for (int k1 = 0; k1 <= ksum; ++k1) {
f_[k1][0] = s_n1[k1], tmp = 0;
for (int k2 = 1; k2 + k1 <= ksum; ++k2, tmp = 0) {
for (int q = 1; q < k2; ++q)
tmp += co[k2][q] * f_[k1][k2-q];
f_[k1][k2] = (k2 * g_[k1][k2-1] + PP - tmp % P) % P;
}
}
for (int k1 = 0; k1 <= ksum; ++k1)
for (int k2 = 0; k2 + k1 <= ksum; ++k2) {
ap = bp = 1, tmp = 0;
for (int e1 = 0; e1 <= k2; ++e1, (ap *= alp) %= P, bp = 1)
for (int e2 = 0; e2 + e1 <= k2; ++e2, (bp *= bta) %= P)
tmp += tri[k2][e1][e2] * ap % P * bp
% P * f_[k1+e1][k2-e1-e2] % P;
f[k1][k2] = tmp % P;
}
for (int k1 = 0; k1 <= ksum; ++k1)
for (int k2 = 0; k2 + k1 + 1 <= ksum; ++k2) {
for (int q = tmp = 0; q <= k2; ++q)
tmp += co[k2+1][q] * f[k1][k2+1-q] % P;
g[k1][k2] = tmp % P * inv[k2+1] % P;
}
}
int main () {
scanf ("%d", &T);
while (T--) {
scanf ("%d%d%d%d%d%d", &n, &a, &b, &c, &k1, &k2);
ksum = k1 + k2; calc (n, a, b, c);
printf ("%lld\n", f[k1][k2]);
}
return 0;
}