一些多项式以及生成函数的题目
Luogu P4389 付公主的背包
Link
Description
有 n n n 种物品,每种物品体积为 v v v ,每种商品有无数个,求凑出体积为 m m m 的方案数。
1 ≤ n , m ≤ 1 0 5 , 1 ≤ v i ≤ m 1\le n,m \le 10^5,1\le v_i \le m 1 ≤ n , m ≤ 1 0 5 , 1 ≤ v i ≤ m
Solution
需要一些生成函数的知识。
对于每个物品我们知道他的生成函数长这样
F ( x ) = ∑ i = 0 ∞ x v × i = 1 1 − x v F(x)=\sum_{i=0}^{\infty}x^{v\times i}=\frac{1}{1-x^v}
F ( x ) = i = 0 ∑ ∞ x v × i = 1 − x v 1
就是在 v v v 的倍数处为 1 1 1 ,其余为 0 0 0 。封闭形式只需要向右移动 v v v 位,也就是乘 x v x^v x v 。
暴力来做就把这 n n n 个生成函数卷起来就行了。
∏ i = 1 n F ( x ) \prod_{i=1}^n F(x)
i = 1 ∏ n F ( x )
考虑优化。在麦克劳林级数定义下的 ln \ln ln 可以进行正常运算,我们可以把卷积这种复杂度较高的式子优化为加法。
ln ( ∏ i = 1 n F ( x ) ) = ∑ i = 1 n ln ( F ( x ) ) \ln\ (\prod_{i=1}^n F(x) )=\sum_{i=1}^n\ln(F(x))
ln ( i = 1 ∏ n F ( x ) ) = i = 1 ∑ n ln ( F ( x ) )
设 G ( x ) = ln ( F ( x ) ) G(x)=\ln(F(x)) G ( x ) = ln ( F ( x ) )
大力推式子
G ′ ( x ) = ln ′ ( F ( x ) ) = F ′ ( x ) F ( x ) = F ′ ( x ) ( 1 − x v ) = ∑ i = 0 ∞ v i x v i − 1 ( 1 − x v ) = ∑ i = 0 ∞ v i x v i − 1 − ∑ i = 0 ∞ v x v ( i + 1 ) − 1 = ∑ i = 1 ∞ v i x v i − 1 − ∑ i = 1 ∞ v ( i − 1 ) x v i − 1 = ∑ i = 1 ∞ v x v i − 1 G ( x ) = ∫ G ′ ( x ) d x = ∑ i = 1 ∞ x v i i \begin{aligned}
G'(x)&=\ln'(F(x))\\
&=\frac{F'(x)}{F(x)}\\
&=F'(x)(1-x^v)\\
&=\sum_{i=0}^{\infty}vix^{vi-1}(1-x^v)\\
&=\sum_{i=0}^{\infty}vix^{vi-1}-\sum_{i=0}^{\infty}vx^{v(i+1)-1}\\
&=\sum_{i=1}^{\infty}vix^{vi-1}-\sum_{i=1}^{\infty}v(i-1)x^{vi-1}\\
&=\sum_{i=1}^{\infty}vx^{vi-1}\\
G(x)=&\int G'(x)dx=\sum_{i=1}^{\infty}\frac{x^{vi}}{i}
\end{aligned}
G ′ ( x ) G ( x ) = = ln ′ ( F ( x ) ) = F ( x ) F ′ ( x ) = F ′ ( x ) ( 1 − x v ) = i = 0 ∑ ∞ v i x v i − 1 ( 1 − x v ) = i = 0 ∑ ∞ v i x v i − 1 − i = 0 ∑ ∞ v x v ( i + 1 ) − 1 = i = 1 ∑ ∞ v i x v i − 1 − i = 1 ∑ ∞ v ( i − 1 ) x v i − 1 = i = 1 ∑ ∞ v x v i − 1 ∫ G ′ ( x ) d x = i = 1 ∑ ∞ i x v i
答案即为
∏ i = 1 n F ( x ) = ∏ i = 1 n e G ( x ) = e ∑ i = 1 n G ( x ) \prod_{i=1}^n F(x)=\prod_{i=1}^n e^{G(x)} = e^{\sum_{i=1}^nG(x)}
i = 1 ∏ n F ( x ) = i = 1 ∏ n e G ( x ) = e ∑ i = 1 n G ( x )
我们只需要多项式的前 m m m 项,O ( m ) O(m) O ( m ) 求 ∑ i = 1 n G ( x ) \sum_{i=1}^n G(x) ∑ i = 1 n G ( x ) ,O ( m log m ) O(m\log m) O ( m log m ) 求多项式 exp,总复杂度 O ( m log m ) O(m\log m) O ( m log m ) 。
Code
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 int n, m, tab[N];ll inv[N], f[N << 2 ], g[N << 2 ]; int main () { read (n), read (m); for (int i = 1 , v; i <= n; i++) read (v), tab[v]++; inv[1 ] = 1 ; for (int i = 2 ; i <= m; i++) inv[i] = (p - p / i) * inv[p % i] % p; for (int i = 1 ; i <= m; i++) { if (!tab[i]) continue ; ll tmp = i * tab[i] % p; for (int j = i; j <= m; j += i) g[j] = add (g[j] + tmp); } for (int i = 0 ; i <= m; i++) g[i] = g[i] * inv[i] % p; Exp (g, f, m + 1 ); for (int i = 1 ; i <= m; i++) write (f[i]), pc ('\n' ); return 0 ; }
Luogu P4841 [集训队作业2013]城市规划
Link
Description
求 n n n 个点的的简单 (无重边无自环) 有标号无向连通图个数。
n ≤ 130000 n\le 130000 n ≤ 1 3 0 0 0 0
Solution
设 f ( n ) f(n) f ( n ) 表示 n n n 个点的的简单有标号无向连通图个数,g ( n ) g(n) g ( n ) 表示 n n n 个点的有标号无向图个数。
显然 g ( n ) = 2 ( n 2 ) g(n)=2^{\binom{n}{2}} g ( n ) = 2 ( 2 n ) ,因为共有 ( n 2 ) \binom{n}{2} ( 2 n ) 条边,每条边都可以选或不选。
g ( n ) = ∑ i = 1 n ( n − 1 i − 1 ) f ( i ) g ( n − i ) g(n)=\sum_{i=1}^n\binom{n-1}{i-1}f(i)g(n-i)
g ( n ) = i = 1 ∑ n ( i − 1 n − 1 ) f ( i ) g ( n − i )
可以理解成枚举 1 1 1 所在的连通块有几个点。
2 ( n 2 ) = ∑ i = 1 n ( n − 1 i − 1 ) f ( i ) g ( n − i ) 2 ( n 2 ) = ∑ i = 1 n ( n − 1 i − 1 ) f ( i ) 2 ( n − i 2 ) 2 ( n 2 ) ( n − 1 ) ! = ∑ i = 1 n f ( i ) 2 ( n − i 2 ) ( i − 1 ) ! ( n − i ) ! 2^{\binom{n}{2}}=\sum_{i=1}^n\binom{n-1}{i-1}f(i)g(n-i) \\
2^{\binom{n}{2}}=\sum_{i=1}^n\binom{n-1}{i-1}f(i)2^{\binom{n-i}{2}} \\
\dfrac{2^{\binom{n}{2}}}{(n-1)!}=\sum_{i=1}^n\dfrac{f(i)2^{\binom{n-i}{2}}}{(i-1)!(n-i)!}
2 ( 2 n ) = i = 1 ∑ n ( i − 1 n − 1 ) f ( i ) g ( n − i ) 2 ( 2 n ) = i = 1 ∑ n ( i − 1 n − 1 ) f ( i ) 2 ( 2 n − i ) ( n − 1 ) ! 2 ( 2 n ) = i = 1 ∑ n ( i − 1 ) ! ( n − i ) ! f ( i ) 2 ( 2 n − i )
然后我们其实已经有卷积的形式了。
设
F ( x ) = ∑ n = 1 ∞ f ( n ) ( n − 1 ) ! G ( x ) = ∑ n = 1 ∞ 2 ( n 2 ) n ! H ( x ) = ∑ n = 1 ∞ 2 ( n 2 ) ( n − 1 ) ! F(x)=\sum_{n=1}^\infin\dfrac{f(n)}{(n-1)!} \\
G(x)=\sum_{n=1}^\infin\dfrac{2^{\binom{n}{2}}}{n!} \\
H(x)=\sum_{n=1}^\infin\dfrac{2^{\binom{n}{2}}}{(n-1)!}
F ( x ) = n = 1 ∑ ∞ ( n − 1 ) ! f ( n ) G ( x ) = n = 1 ∑ ∞ n ! 2 ( 2 n ) H ( x ) = n = 1 ∑ ∞ ( n − 1 ) ! 2 ( 2 n )
那么
H ( x ) = F ( x ) ∗ G ( x ) F ( x ) = H ( x ) G ( x ) H(x)=F(x)*G(x) \\
F(x)=\dfrac{H(x)}{G(x)}
H ( x ) = F ( x ) ∗ G ( x ) F ( x ) = G ( x ) H ( x )
最后的答案即为 F ( x ) F(x) F ( x ) 中 x n x^n x n 的系数乘 ( n − 1 ) ! (n-1)! ( n − 1 ) !
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int n;ll fac[N]; ll f[N << 2 ], g[N << 2 ], gi[N << 2 ]; int main () { read (n), n++; fac[0 ] = 1 , g[0 ] = 1 ; for (int i = 1 ; i < n; i++) fac[i] = fac[i - 1 ] * i % p; for (int i = 1 ; i < n; i++) f[i] = qpow (2 , 1ll * i * ( i - 1 ) / 2 % (p - 1 )) * qpow (fac[i - 1 ], p - 2 ) % p; for (int i = 1 ; i < n; i++) g[i] = f[i] * qpow (i, p - 2 ) % p; Inv (g, gi, n); Mul (f, gi, n, n); write (f[n - 1 ] * fac[n - 2 ] % p), pc ('\n' ); return 0 ; }
Luogu P4723 【模板】常系数齐次线性递推
Link
Description
求一个满足 k k k 阶齐次线性递推数列 a i {a_i} a i 的第 n n n 项,即:
a n = ∑ i = 1 k f i × a n − i a_n=\sum\limits_{i=1}^{k}f_i \times a_{n-i}
a n = i = 1 ∑ k f i × a n − i
对 998244353 998244353 9 9 8 2 4 4 3 5 3 取模
n = 1 0 9 , k = 32000 n=10^9,k=32000 n = 1 0 9 , k = 3 2 0 0 0
Solution
懒得写了,直接放一下连接吧(,讲的太好了
题解 P4723 【模板】常系数齐次线性递推
但是题解区代码没一个能看的(
主要是注意一下边界,在乘法后项数会多一倍,所以取模时传的数组长度要为 2 m 2m 2 m 。
不知道为啥常数非常大,不开 O2 一个点都过不去,可能是人傻常数大吧 qaq
Code
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 int n, m;ll p[N], f[N], pr[N], pi[N]; ll a[N], b[N]; void Mod (ll *a, ll *b, int n, int m) { int lim = calclim (n << 1 ); Copy (a, b, n), Clear (b, n, lim), reverse (b, b + n); Mul (b, pi, n, n - m + 2 ), Clear (b, n - m + 1 , n + n - m + 2 ), reverse (b, b + n - m + 1 ); Mul (b, p, n - m + 2 , m); for (int i = 0 ; i < m - 1 ; i++) b[i] = sub (a[i] - b[i]); return ; } ll r[N]; void MulMod (ll *a, ll *b, int n, int m) { int lim = calclim (n << 1 ); Clear (r, 0 , lim); Mul (a, b, n, m); Mod (a, r, n + m, m); Copy (r, a, m - 1 ), Clear (a, m - 1 , lim); return ; } void Qpow (ll *a, ll *b, int k) { b[0 ] = 1 ; while (k) { if (k & 1 ) MulMod (b, a, m, m); MulMod (a, a, m, m), k >>= 1 ; } return ; } int main () { read (n), read (m); for (int i = 1 ; i <= m; i++) read (pr[i]), pr[i] = sub (mod - pr[i] % mod), p[m - i] = pr[i]; for (int i = 0 ; i < m; i++) read (f[i]); p[m] = pr[0 ] = 1 , m++; Inv (pr, pi, m + 2 ); a[1 ] = 1 ; Qpow (a, b, n); ll ans = 0 ; for (int i = 0 ; i < m; i++) ans = (ans + f[i] * b[i] % mod + mod) % mod; write (ans), pc ('\n' ); return 0 ; }
CF438E The Child and Binary Tree
Link
Description
太长不想写(
Solution
设 f i f_i f i 表示权值为 i i i 的神犇二叉树的个数,即为所求,g i g_i g i 表示有没有权值为 i i i 的点。
g i = 0 / 1 f n = 1 ( n = 0 ) f n = ∑ i = 1 n g i ∑ j = i n − i f j f n − i − j ( n > 0 ) g_i=0/1\\
f_n=1\ (n=0) \\
f_n=\sum_{i=1}^ng_i\sum_{j=i}^{n-i}f_{j}f_{n-i-j}\ (n>0) \\
g i = 0 / 1 f n = 1 ( n = 0 ) f n = i = 1 ∑ n g i j = i ∑ n − i f j f n − i − j ( n > 0 )
可以发现这是三个多项式卷在一起
设 F F F 表示 f f f 的生成函数,G G G 表示 g g g 的生成函数
F = G ∗ F 2 + 1 F=G*F^2+1
F = G ∗ F 2 + 1
这里 + 1 +1 + 1 是因为 f 0 = 1 f_0=1 f 0 = 1 。
根据求根公式
F = 1 ± 1 − 4 G 2 G F=\dfrac{1\pm \sqrt{1-4G}}{2G}
F = 2 G 1 ± 1 − 4 G
分类讨论一下 ± \pm ± ,发现只能是 − - − ,如果发现不了就都试一试就行了(
F = 1 − 1 − 4 G 2 G F=\dfrac{1-\sqrt{1-4G}}{2G}
F = 2 G 1 − 1 − 4 G
分子分母同时 × ( 1 + 1 − 4 G ) \times (1+\sqrt{1-4G}) × ( 1 + 1 − 4 G ) ,得
F = 2 1 + 1 − 4 G F=\dfrac{2}{1+\sqrt{1-4G}}
F = 1 + 1 − 4 G 2
这波分子无理化也属实 nb,因为 2 G 2G 2 G 的常数项为 0 0 0 ,并不能求逆,而后面的式子有个 + 1 +1 + 1 。
然后就是多项式求逆和多项式开根板子了
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int n, m;ll f[N << 2 ], g[N << 2 ], sg[N << 2 ]; int main () { read (n), read (m), m++; for (int i = 1 , c; i <= n; i++) read (c), g[c] = 1 ; g[0 ] = 1 ; for (int i = 1 ; i < m; i++) g[i] = p - 4 * g[i] % p; Sqrt (g, sg, m); sg[0 ]++; Inv (sg, f, m); for (int i = 0 ; i < m; i++) f[i] = f[i] * 2 % p; for (int i = 1 ; i < m; i++) write (f[i]), pc ('\n' ); pc ('\n' ); return 0 ; }
CF528D Fuzzy Search
Link
Description
给定两个只含 AGCT 的基因串 S S S 和 T T T ,求在下列规则中 T T T 在 S S S 中出现了几次。
T T T 在 S S S 的第 i i i 个位置中出现,当且仅当把 T T T 的首字符和 S S S 的第 i i i 个字符对齐后,T T T 中的每一个字符能够在 S S S 中找到一个位置偏差不超过 k k k 的相同字符。
1 ≤ n ≤ m ≤ 2 × 1 0 5 , 0 ≤ k ≤ 2 × 1 0 5 1\le n\le m \le 2\times 10^5,0\le k \le 2\times 10^5 1 ≤ n ≤ m ≤ 2 × 1 0 5 , 0 ≤ k ≤ 2 × 1 0 5
Solution
因为只有四种字符,我们不妨对每个字符分别考虑,如果四种字符都合法,那么这个位置就是合法的。
对于偏差不超过 k k k ,并不太好处理,但是我们只对一个字符考虑,所以可以将可以为该字符的位置赋成 1 1 1 ,其余的都赋成 0 0 0 ,即把在偏差 k k k 以内的都改成该字符。
然后我们将得到的新的 S S S 和 T T T 分别设为 f f f 和 g g g ,都是 01 01 0 1 串。
h i = ∑ j = 0 m − 1 g j ( f i + j − g j ) 2 h_i=\sum_{j=0}^{m-1}g_j(f_{i+j}-g_j)^2
h i = j = 0 ∑ m − 1 g j ( f i + j − g j ) 2
如果 h i = 0 h_i=0 h i = 0 ,就说明可以匹配。
平方是为了不会出现 1 + ( − 1 ) = 0 1+(-1)=0 1 + ( − 1 ) = 0 的情况
h i = ∑ j = 0 m − 1 g j ( f i + j − g j ) 2 = ∑ j = 0 m − 1 g j f i + j 2 − g j 3 − 2 g j 2 f i + j = ∑ j = 0 m − 1 g j − g j f i + j \begin{aligned}
h_i&=\sum_{j=0}^{m-1}g_j(f_{i+j}-g_j)^2 \\
&=\sum_{j=0}^{m-1}g_jf_{i+j}^2-g_j^3-2g_j^2f_{i+j} \\
&=\sum_{j=0}^{m-1}g_j-g_jf_{i+j}
\end{aligned}
h i = j = 0 ∑ m − 1 g j ( f i + j − g j ) 2 = j = 0 ∑ m − 1 g j f i + j 2 − g j 3 − 2 g j 2 f i + j = j = 0 ∑ m − 1 g j − g j f i + j
这里是因为 f , g f,g f , g 都为 01 01 0 1 串,所以三次方和平方都可以改为一次。
然后就套路地将 g g g 翻转,就可以卷积了
h i = ∑ j = 0 m − 1 g m − 1 − j − g m − 1 − j f i + j h_i=\sum_{j=0}^{m-1}g_{m-1-j}-g_{m-1-j}f_{i+j}
h i = j = 0 ∑ m − 1 g m − 1 − j − g m − 1 − j f i + j
Code
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 int n, m, k, lim;char s[N], t[N];ll f[N << 2 ], g[N << 2 ], h[N << 2 ]; int len[N];void solve (char c) { memset (f, 0 , sizeof (f)); memset (g, 0 , sizeof (g)); memset (h, 0 , sizeof (h)); for (int i = 0 ; i < n; i++) if (s[i] == c) f[i - k < 0 ? 0 : i - k]++, f[i + k + 1 ]--; for (int i = 0 ; i < n; i++) f[i] += f[i - 1 ]; for (int i = 0 ; i < n; i++) if (f[i]) f[i] = 1 ; for (int i = 0 ; i < m; i++) g[i] = t[m - i - 1 ] == c; Mul (f, g, n, m); for (int i = 0 ; i < n; i++) len[i] += f[i]; return ; } int main () { read (n), read (m), read (k); scanf ("%s%s" , s, t); solve ('A' ), solve ('C' ), solve ('G' ), solve ('T' ); int ans = 0 ; for (int i = 0 ; i < n; i++) ans += (len[i] == m); write (ans), pc ('\n' ); return 0 ; }
Luogu P4173 残缺的字符串
Link
Description
给定一个长度为 n n n 的字符串 A A A 和一个长度为 m m m 的字符串 B B B ,仅由小写字母和 * 组成,其中 * 表示相应位置已经残缺,即可以为任意字母。
求对于 B B B 的每一个位置 i i i ,从这个位置开始连续 n n n 个字符形成的子串是否可能与 A A A 匹配。
1 ≤ n ≤ m ≤ 3 × 1 0 5 1\le n\le m \le 3\times 10^5 1 ≤ n ≤ m ≤ 3 × 1 0 5
Solution
经典通配符匹配问题
将 * 的位置赋成 0。
设 A A A 和 B B B 的差异为
d ( A , B ) = ∑ i = 0 n − 1 ( A i − B i ) 2 A i B i d(A,B)=\sum_{i=0}^{n-1}(A_i-B_i)^2A_iB_i
d ( A , B ) = i = 0 ∑ n − 1 ( A i − B i ) 2 A i B i
那么 B B B 以 i i i 结尾的子串与 A A A 完全匹配的条件为
f ( i ) = d ( A , B [ i − m + 1 , i ] ) = 0 f(i)=d(A,B[i-m+1,i])=0
f ( i ) = d ( A , B [ i − m + 1 , i ] ) = 0
比较套路地翻转 A A A ,并写成卷积的形式
f ( i ) = ∑ j = 0 i ( A i − j − B j ) 2 A i − j B j = ∑ j = 0 i A i − j 3 B j − A i − j 2 B j 2 + A i − j B j 3 \begin{aligned}
f(i)&=\sum_{j=0}^{i}(A_{i-j}-B_j)^2A_{i-j}B_j \\
&=\sum_{j=0}^iA_{i-j}^3B_j-A_{i-j}^2B_j^2+A_{i-j}B_j^3
\end{aligned}
f ( i ) = j = 0 ∑ i ( A i − j − B j ) 2 A i − j B j = j = 0 ∑ i A i − j 3 B j − A i − j 2 B j 2 + A i − j B j 3
然后就是三个卷积了,这里写的 FFT
Code
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 int n, m;char S[N], T[N];int s[N], t[N];Complex a[N << 2 ], b[N << 2 ], c[N << 2 ]; int main () { read (n), read (m); scanf ("%s%s" , S, T); for (int i = 0 ; i < n; i++) if (S[i] != '*' ) s[i] = S[i] - 'a' + 1 ; for (int i = 0 ; i < m; i++) if (T[i] != '*' ) t[i] = T[i] - 'a' + 1 ; reverse (s, s + n); int lim = calclim (n + m); calcrev (lim); for (int i = 0 ; i < lim; i++) a[i] = b[i] = Complex (0.0 , 0.0 ); for (int i = 0 ; i < n; i++) a[i] = Complex (s[i] * s[i] * s[i], 0 ); for (int i = 0 ; i < m; i++) b[i] = Complex (t[i], 0 ); FFT (a, lim, 1 ), FFT (b, lim, 1 ); for (int i = 0 ; i < lim; i++) c[i] = a[i] * b[i]; for (int i = 0 ; i < lim; i++) a[i] = b[i] = Complex (0.0 , 0.0 ); for (int i = 0 ; i < n; i++) a[i] = Complex (s[i] * s[i], 0 ); for (int i = 0 ; i < m; i++) b[i] = Complex (t[i] * t[i], 0 ); FFT (a, lim, 1 ), FFT (b, lim, 1 ); for (int i = 0 ; i < lim; i++) c[i] = c[i] - a[i] * b[i] * 2.0 ; for (int i = 0 ; i < lim; i++) a[i] = b[i] = Complex (0.0 , 0.0 ); for (int i = 0 ; i < n; i++) a[i] = Complex (s[i], 0 ); for (int i = 0 ; i < m; i++) b[i] = Complex (t[i] * t[i] * t[i], 0 ); FFT (a, lim, 1 ), FFT (b, lim, 1 ); for (int i = 0 ; i < lim; i++) c[i] = c[i] + a[i] * b[i]; FFT (c, lim, -1 ); int ans = 0 ; for (int i = n - 1 ; i < m; i++) if (fabs (c[i].x) < 0.5 ) ans++; write (ans), pc ('\n' ); for (int i = n - 1 ; i < m; i++) if (fabs (c[i].x) < 0.5 ) write (i - n + 2 ), pc (' ' ); pc ('\n' ); return 0 ; }
Luogu P3723 [AH2017/HNOI2017]礼物
Link
Description
给定两个长度为 n n n 的数组 a a a 和 b b b ,求 ∑ i = 1 n ( a i + x − b i ) 2 \sum_{i=1}^n(a_i+x-b_i)^2 ∑ i = 1 n ( a i + x − b i ) 2 的最小值。
1 ≤ n ≤ 50000 , 1 ≤ a i ≤ m ≤ 100 1\le n \le 50000,1\le a_i \le m \le 100 1 ≤ n ≤ 5 0 0 0 0 , 1 ≤ a i ≤ m ≤ 1 0 0
Solution
将式子展开
∑ i = 1 n ( a i + x − b i ) 2 = ∑ i = 1 n ( a i 2 + b i 2 + x 2 + 2 a i x − 2 a i b i − 2 b i x ) = ∑ i = 1 n a i 2 + ∑ i = 1 n b i 2 + ∑ i = 1 n x 2 + 2 x ∑ i = 1 n ( a i − b i ) − 2 ∑ i = 1 n a i b i \begin{aligned}
&\sum_{i=1}^n(a_i+x-b_i)^2 \\
=&\sum_{i=1}^n(a_i^2+b_i^2+x^2+2a_ix-2a_ib_i-2bi_x) \\
=&\sum_{i=1}^na_i^2+\sum_{i=1}^nb_i^2+\sum_{i=1}^nx^2+2x\sum_{i=1}^n(a_i-b_i)-2\sum_{i=1}^na_ib_i
\end{aligned}
= = i = 1 ∑ n ( a i + x − b i ) 2 i = 1 ∑ n ( a i 2 + b i 2 + x 2 + 2 a i x − 2 a i b i − 2 b i x ) i = 1 ∑ n a i 2 + i = 1 ∑ n b i 2 + i = 1 ∑ n x 2 + 2 x i = 1 ∑ n ( a i − b i ) − 2 i = 1 ∑ n a i b i
一二项是定值,三四项就是个二次函数,只需要找到对称轴,然后找到两边最近的两个整数,算一下最小值即可。
x = ∑ ( b i − a i ) n x=\dfrac{\sum(b_i-a_i)}{n}
x = n ∑ ( b i − a i )
现在只需要将第五项最大化
也是套路地翻转一下 b b b 数组,然后就可以卷积了,要将 a a a 数组复制一倍,卷积后求 x n x^n x n 到 x 2 n − 1 x^{2n-1} x 2 n − 1 的系数的最大值。
Code
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 int n, m;ll a[N], b[N], sa, sb, ans; ll calc (int x) { return n * x * x + 2 * x * (sa - sb); } int main () { read (n), read (m); for (int i = 0 ; i < n; i++) read (a[i]), a[i + n] = a[i], sa += a[i], ans += a[i] * a[i]; for (int i = 0 ; i < n; i++) read (b[i]), sb += b[i], ans += b[i] * b[i]; int x1 = floor (1.0 * (sb - sa) / n), x2 = ceil (1.0 * (sb - sa) / n); ans += min (calc (x1), calc (x2)); reverse (b, b + n); Mul (a, b, n + n, n); ll mx = -1e18 ; for (int i = n; i < n * 2 ; i++) mx = max (mx, a[i]); ans -= (mx << 1 ); write (ans), pc ('\n' ); return 0 ; }