高次类欧几里得算法

LibreOJ #138 类欧几里得算法

题意

T1000T \leq 1000 组数据。给定 n,a,b,c[0,109],k1,k2[0,10],k1+k210n, a, b, c\in [0, 10^9], k_1,k_2\in [0, 10], k_1+k_2\leq 10,求取 x=0nxk1ax+bck2 \sum_{x=0}^{n}x^{k_1}\left\lfloor\frac{ax+b}{c}\right\rfloor^{k_2}

部分分——观察性质

k1=0,k2=1k_1=0,k_2=1 时,是为一般类欧几里得算法的模板。上述链接亦给出了 k1=0,k2=2;k1=1,k2=1k_1=0,k_2=2; k_1=1,k_2=1 时的解法。

k2=0k_2=0 时,该式退化为 x=0nxk1\sum_{x=0}^{n}x^{k_1},即 k1k_1 次的等幂求和。有一个模糊的结论:

nn 为正整数。则kk 次的等幂求和,x=0nxk\sum_{x=0}^{n}x^k,是一个关于 nnk+1k+1 次多项式。

该结论来源于 zyw 学姐多项式专题所选题目 BZOJ 3453 – tyvj 1858 XLkxc。之所以说模糊,是因为该多项式的系数是已知的。不过我们仍然可以暴力计算 k+2k+2 个点以插值。

再观察 OI-Wiki 上对于 k1+k22k_1+k_2\leq 2 时的解法。在求取 h(n,a,b,c)=x=0nax+bc2h(n,a,b,c)=\sum_{x=0}^n\left\lfloor\frac{ax+b}{c}\right\rfloor^2时采取了如下转化:

x2=2×x(x+1)2x=2(y=1xy)xx=0nax+bc2=x=0n2(y=1ax+bcy)ax+bc\begin{aligned} x^2&=2\times \frac{x(x+1)}{2}-x\\ &=2\left(\sum_{y=1}^{x}y\right)-x\end{aligned} \quad\begin{aligned} \Longrightarrow\sum_{x=0}^n\left\lfloor\frac{ax+b}{c}\right\rfloor^2=\sum_{x=0}^n2\left(\sum_{y=1}^{\left\lfloor\frac{ax+b}{c}\right\rfloor}y\right)-\left\lfloor\frac{ax+b}{c}\right\rfloor\\ \end{aligned}

这样一来,由于 yy 是一个线性算子,在 ax+bycax+b\geq yc 时都有 yy 向总和贡献,于是我们可以应用类似于 k1=0,k2=1k_1=0,k_2=1 时的方法转化贡献。这个过程对 k2k_2 作了降次。

那么,对于更高次项,有无办法采取同样的办法转化呢?是否可以设计一些转化,使得要求取的函数 f(,k1,k2)f(\cdots,k_1,k_2) 能够由 f(,k1?,k2?)f(\cdots,k_1-?,k_2-?) 推出呢?

伯努利数

B010={1,12,16,0,130,0,142,0,130,0,566}B_{0\dots 10}=\left\{1,-\frac{1}{2},\frac{1}{6},0,-\frac{1}{30},0,\frac{1}{42},0,-\frac{1}{30},0,\frac{5}{66}\right\}

Sm(n)=x=0n1xmS_m(n)=\sum\limits_{x=0}^{\color{red}n-1}x^m,则有如下性质: Sm(n)=1m+1k=0m(m+1k)Bknm+1k S_m(n)=\frac{1}{m+1}\sum_{k=0}^{m}\binom{m+1}{k}B_kn^{m+1-k}

用红笔标注是因为第二版推导的“等幂求和”定义是 x=1nxm\sum_{x=1}^{n}x^m(OI-Wiki 的转化——在下文中出现的那个——用的是这种)。最终检查时才发现不对劲。

(不过,将 B1B_1 改为 12\frac{1}{2} 的情况下,应用“错误”的“等幂求和”公式得到的结果是正确的,似乎也能计算。但这会牵扯到 x=0x=0 时的计算 和 00=10^0=1 的定义问题,有点麻烦。暂且按下不表。)

我们移项可得 (m+1)Sm(n)=(m+10)B0nm+1+k=1m(m+1k)Bknm+1knm+1=(m+1)Sm(n)k=1m(m+1k)Bknm+1knm=mSm1(n)k=1m1(mk)Bknmk\begin{aligned} (m+1)S_m(n)&=\binom{m+1}{0}B_0n^{m+1}+\sum_{k=1}^{m}\binom{m+1}{k}B_kn^{m+1-k}\\ n^{m+1}&=(m+1)S_m(n)-\sum_{k=1}^{m}\binom{m+1}{k}B_kn^{m+1-k}\\ n^{m}&=mS_{m-1}(n)-\sum_{k=1}^{m-1}\binom{m}{k}B_kn^{m-k} \end{aligned}

上式右侧是一个等幂求和以及若干 nk,k<mn^k, \underline{k<m} 与常数项之积的和。当 m>1m>1 时,若能够快速处理 mSm1(n)mS_{m-1}(n) 这一部分的贡献,便能使用 k2k_2 更小时的函数值计算。这便是处理 ax+bcm\lfloor\frac{ax+b}{c}\rfloor^m 的转化的一般形式。

推导过程

类似于一般类欧的思路,我们尽可能将 (a,b,c)(a,b,c) 的情况化归为 (amodc,?,c)(a\bmod c,?,c) 再转化贡献。

推导 ff

f(n,a,b,c,k1,k2)=x=0nxk1ax+bck2f(n,a,b,c,k_1,k_2)=\sum_{x=0}^{n}x^{k_1}\left\lfloor\frac{ax+b}{c}\right\rfloor^{k_2}

α=ac,β=bc,a=amodc,b=bmodc\alpha=\lfloor\frac{a}{c}\rfloor,\beta=\lfloor\frac{b}{c}\rfloor,a’=a\bmod c,b’=b\bmod c。则 原式=x=0nxk1(αc+a)x+(βc+b)ck2=x=0nxk1αx+β+ax+bck2=x=0nxk1(αx+β+ax+bc)k2(整数自由移出取整号)=x=0nxk1e1+e2+e3=k2e1,e2,e3N(k1e1,e2,e3)(αx)e1βe2ax+bce3=e1+e2+e3=k2e1,e2,e3Nαe1βe2(k2e1,e2,e3)x=0nxk1+e1ax+bce3\begin{aligned} \text{原式}&=\sum_{x=0}^{n}x^{k_1}\left\lfloor\frac{(\alpha c+a’)x+(\beta c+b’)}{c}\right\rfloor^{k_2}\\ &=\sum_{x=0}^{n}x^{k_1}\left\lfloor\alpha x+\beta+\frac{a’x+b’}{c}\right\rfloor^{k_2}\\ &=\sum_{x=0}^{n}x^{k_1}\left(\alpha x+\beta+\left\lfloor\frac{a’x+b’}{c}\right\rfloor\right)^{k_2}&\text{(整数自由移出取整号)}\\ &=\sum_{x=0}^{n}x^{k_1}\sum_{\substack{e_1+e_2+e_3=k2\\e_1,e_2,e_3\in \mathbb{N}}}\binom{k1}{e_1,e_2,e_3}(\alpha x)^{e_1}\beta^{e_2}\left\lfloor\frac{a’x+b’}{c}\right\rfloor^{e_3}\\ &=\sum_{\substack{e_1+e_2+e_3=k2\\e_1,e_2,e_3\in \mathbb{N}}}\alpha^{e_1}\beta^{e_2}\binom{k2}{e_1,e_2,e_3}\sum_{x=0}^{n}x^{k_1+e_1}\left\lfloor\frac{a’x+b’}{c}\right\rfloor^{e_3} \end{aligned}

(k2e1,e2,e3)\binom{k_2}{e_1,e_2,e_3}多项式系数。有 (nm1,m2,m3)=n!m1!m2!m3!\binom{n}{m_1,m_2,m_3}=\frac{n!}{m_1!m_2!m_3!},是为 (a+b+c)n(a+b+c)^n 展开式中 am1bm2cm3a^{m_1}b^{m_2}c^{m_3} 的系数。

推导 ff’

f(n,a,b,c,k1,k2)=x=0nxk1ax+bck2(a,b[0,c))f'(n,a’,b’,c,k_1,k_2)=\sum_{x=0}^{n}x^{k_1}\left\lfloor\frac{a’x+b’}{c}\right\rfloor^{k_2}\quad(a’,b’\in[0,c))

tx=ax+bct_x=\lfloor\frac{a’x+b’}{c}\rfloor,则 原式=x=0nxk1(k2Sk21(tx)q=1k21(k2q)Bqtxk2q)(等幂求和公式变形)=k2(x=0nxk1Sk21(tx))q=1k21(k2q)Bqx=0nxk1txk2q=k2(x=0nxk1Sk21(tx))q=1k21(k2q)Bqf(n,a,b,c,k1,k2q)\begin{aligned} \text{原式}&=\sum_{x=0}^{n}x^{k_1}\left(k_2S_{k_2-1}(t_x)-\sum_{q=1}^{k_2-1}\binom{k_2}{q}B_qt_x^{k_2-q}\right)&\text{(等幂求和公式变形)}\\ &=k_2\left(\sum_{x=0}^{n}x^{k_1}S_{k_2-1}(t_x)\right)-\sum_{q=1}^{k_2-1}\binom{k_2}{q}B_q\sum_{x=0}^{n}x^{k_1}t_x^{k_2-q}\\ &=k_2\left(\sum_{x=0}^{n}x^{k_1}S_{k_2-1}(t_x)\right)-\sum_{q=1}^{k_2-1}\binom{k_2}{q}B_qf'(n,a’,b’,c,k_1,k_2-q) \end{aligned}

右侧可以由 k2<k2k_2′<k_2 的函数值求出;现在考虑如何转化左侧的贡献。

推导 gg’

g(n,a,b,c,k1,k2)=x=0nxk1Sk2(ax+bc)(a,b[0,c))=x=0nxk1y=0ax+bc1yk2\begin{aligned} g'(n,a’,b’,c,k_1,k_2)&=\sum_{x=0}^{n}x^{k_1}S_{k_2}\left(\left\lfloor\frac{a’x+b’}{c}\right\rfloor\right)&(a’,b’\in[0,c))\\ &=\sum_{x=0}^{n}x^{k_1}\sum_{y=0}^{\color{red}\lfloor\frac{a’x+b’}{c}\rfloor-1}y^{k_2} \end{aligned}

发现 Sm(n)S_m(n) 是前缀和的形式。则对于固定的 yy,当 ax+bc1y ax+b(y+1)c x>yc+cb1a\begin{aligned} &\left\lfloor\frac{a’x+b’}{c}\right\rfloor-1\geq y\\ \Longrightarrow&\space a’x+b\geq (y+1)c\\ \Longrightarrow&\space x>\left\lfloor\frac{yc+c-b’-1}{a’}\right\rfloor \end{aligned} 时,均有 yk2y^{k_2} 对总和作出贡献。

p=an+bcp=\lfloor\frac{a’n+b’}{c}\rfloor。则 原式=y=0p1yk2x=yc+cb1a+1nxk1=y=0p1yk2(Sk1(n+1)Sk1(yc+cb1a+1))=Sk2(p)Sk1(n+1)y=0p1yk2Sk1(yc+c+ab1a)\begin{aligned} \text{原式}&=\sum_{y=0}^{p-1}y^{k_2}\sum_{x=\lfloor\frac{yc+c-b’-1}{a’}\rfloor+1}^{n}x^{k_1}\\ &=\sum_{y=0}^{p-1}y^{k_2}\left(S_{k_1}(n+1)-S_{k_1}\left(\left\lfloor\frac{yc+c-b’-1}{a’}\right\rfloor{\color{blue}+1}\right)\right)\\ &=S_{k_2}(p)S_{k_1}(n+1)-\sum_{y=0}^{p-1}y^{k_2}S_{k_1}\left(\left\lfloor\frac{yc+c{\color{blue}+a’}-b’-1}{a’}\right\rfloor\right) \end{aligned}

右侧看起来……很眼熟。它像是 g(p1,c,c+ab1,a,k2,k1)g'(p-1,c,c+a’-b’-1,a’,k_2,k_1),但此时不可能有 c[0,a)c\in [0, a)。我们必须在这个唯一利用到下层递归函数值的地方做文章。

推导 gg

g(n,a,b,c,k1,k2)=x=0nxk1Sk2(ax+bc)=x=0nxk1q=0k21k2+1(k2+1q)Bqax+bck2+1q(等幂求和公式)=q=0k21k2+1(k2+1q)Bqx=0nxk1ax+bck2+1q\begin{aligned} g(n,a,b,c,k_1,k_2)&=\sum_{x=0}^{n}x^{k_1}S_{k_2}\left(\left\lfloor\frac{ax+b}{c}\right\rfloor\right)\\ &=\sum_{x=0}^{n}x^{k_1}\sum_{q=0}^{k_2}\frac{1}{k_2+1}\binom{k_2+1}{q}B_q\left\lfloor\frac{ax+b}{c}\right\rfloor^{k_2+1-q}&\text{(等幂求和公式)}\\ &=\sum_{q=0}^{k_2}\frac{1}{k_2+1}\binom{k_2+1}{q}B_q\sum_{x=0}^{n}x^{k_1}\left\lfloor\frac{ax+b}{c}\right\rfloor^{k_2+1-q} \end{aligned}

右侧终于出现了我们所熟知的 f(n,a,b,c,k1,k2+1q)f(n,a,b,c,k_1,k_2+1-q)。可以发现在同一层中 ff 的计算仅仅依赖 gg’ 而非 gggg’ 调用的是下层递归的 gg,故我们已经完成了计算。

最终的递归式子

结合上文,我们可以立刻得到: f(n,a,b,c,k1,k2)=e1+e2+e3=k2e1,e2,e3Nαe1βe2(k2e1,e2,e3)f(n,a,b,c,k1+e1,k2)f(n,a,b,c,k1,k2)=k2g(n,a,b,c,k1,k21)q=1k21(k2q)Bqf(n,a,b,c,k1,k2q)g(n,a,b,c,k1,k2)=Sk2(p)Sk1(n+1)g(p1,c,c+ab1,a,k2,k1)(p=an+bc)g(n,a,b,c,k1,k2)=q=0k21k2+1(k2+1q)Bqf(n,a,b,c,k1,k2+1q)\begin{aligned} f(n,a,b,c,k_1,k_2)&=\sum_{\substack{e_1+e_2+e_3=k_2\\e_1,e_2,e_3\in\mathbb{N}}}\alpha^{e_1}\beta^{e_2}\binom{k_2}{e_1,e_2,e_3}f'(n,a’,b’,c,k_1+e_1,k_2)\\ f'(n,a’,b’,c,k_1,k_2)&=k_2g'(n,a’,b’,c,k_1,k_2-1)-\sum_{q=1}^{k_2-1}\binom{k_2}{q}B_qf'(n,a’,b’,c,k_1,k_2-q)\\ g'(n,a’,b’,c,k_1,k_2)&=S_{k_2}(p)S_{k_1}(n+1)-g(p-1,c,c+a’-b’-1,a’,k_2,k_1)&(p=\left\lfloor\frac{a’n+b’}{c}\right\rfloor)\\ g(n,a,b,c,k_1,k_2)&=\sum_{q=0}^{k_2}\frac{1}{k_2+1}\binom{k_2+1}{q}B_qf(n,a,b,c,k_1,k_2+1-q) \end{aligned}

边界条件有 n<0c=0n<0\lor c=0 时返回 00;同时 f(n,a,b,c,k1,k2=0)=Sk1(n+1)f'(n,a,b,c,k_1,k_2=0)=S_{k_1}(n+1),不需要(也不能——因为 S1(n)S_{-1}(n) 是未定义的)递归计算。

慢着。细心的你可能发现了一个问题:我们调用了 g(,k1,k2)g'(\cdots,k_1,k_2)gg’ 调用了下一层的 gg,而 g(,k1,k2)g(\cdots,k_1,k_2) 的计算式里要调用 f(,k1,k2+1q)f(\cdots,k_1,k_2+1-q)q=0q=0 的话岂不是造成了无限递归?!

稍加分析可得,在 f(,k1,k2)f'(\cdots,k_1,k_2) 中我们最多调用 g(,k1,k21)g'(\cdots,k_1,k_2-1),而其调用下一层的 g(,k21,k1)g(\cdots,k_2-1,k_1),此时套入上式得到其最多调用 f(,k21,k1+1)f(\cdots,k_2-1,k_1+1),在此过程中 两参数之和=k2+k1\text{两参数之和}=k_2+k_1,保持不变。故而我们只需计算两参数之和 (题面给定的)k1+k2=ksum\leq\text{(题面给定的)}k_1+k_2=k_\text{sum} 的所有函数值,它便是自洽的。

通过上述式子分析得到,单查询时间复杂度 O(logmax(a,c)(k1+k2)3)\operatorname{O}(\log\max(a,c)(k_1+k_2)^3)。通过预先计算三项式系数、伯努利数与二项式系数之积,以及同层内的 Sk(n),Sk(p),k[0,ksum]S_{k}(n),S_{k}(p),k\in[0,k_\text{sum}],可以优化常数。

提交记录 #1604976

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
  103. 103
  104. 104
  105. 105
  106. 106
  107. 107
  108. 108
  109. 109
  110. 110
  111. 111
  112. 112
  113. 113
  114. 114
  115. 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); // * 计算 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; // * 计算 f'。 for (int k1 = 0; k1 <= ksum; ++k1) { f_[k1][0] = s_n1[k1], tmp = 0; // 单独计算 k2=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; } } // * 计算 f。 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; } // * 计算 g。 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; }
  • 2022年10月13日