Luogu P7468. [NOI Online 2021 提高组] 愤怒的小 N
给定数列 \(\{b_n\}\) 的构造方式:
- \(b_0=0\);
- \(\forall i\in [2^n,2^{n+1}),b_i=1-b_{i-2^{n}}\)。
给定 \(n\) 的二进制表示和一个次数为 \(k-1\) 的多项式 \(F(x)=\sum\limits_{i=0}^{k-1}f_ix^i\),求 \(\sum\limits_{i=0}^{n-1}b_iF(i)\) ,答案对 \(10^9+7\) 取模。
\(1\le \log_2 n\le 5\cdot 10^5,1\le k\le 500,0\le f_i<10^9+7,f_{k-1}\neq 0\)。
首先观察 \(b\) 的构造方式,假设要递归求解 \(b_{t}\),那么就重复删除 \(t\) 的二进制表示中的最高位将值反转,那么可以归纳推导出
最终的和式也就是
考虑对求和的范围进行分段,分成若干长度为 \(2^t\) 的段进行计算。设
以上式子中的 \(x\) 的二进制表示中 \(0\sim t-1\) 位为 \(0\)。
进一步推导
\(B_{t+1}(x)\) 也同理,于是有
式子比较对称,尝试求和求差
分段求和时,\(A_{t+1}(x)+B_{t+1}(x)\) 会将所有 \(F(i)\) 都加起来,也就是 \(\sum\limits_{i=0}^{n-1} F(i)\),这个可以用拉格朗日插值 \(O(k)\) 求出(带入 \(x=0,1,\ldots,k+1\) 维护前后缀积,也可以暴力计算,复杂度为 \(O(k^2)\) 或者 \(O(k^2\log k)\))。
记 \(C_{t}(x)=A_{t}(x)-B_{t}(x)\),则
乍一看直接从 \(C_{0}(x)\) 推到 \(C_{n}(x)\) 是 \(O(nk^2)\) 或者 \(O(nk\log k)\)。但是每次递推时 \(x\) 的最高次项都在 \(C_{t-1}(x)-C_{t-1}(x+2^{t})\) 中被消去了,而最多消去 \(O(k)\) 次,因此复杂度可以做到 \(O(k^3)\) 或者 \(O(k^2\log k)\)。前者是暴力进行多项式平移,后者是使用 \(\text{NTT}\) 优化。
总时间复杂度为 \(O(\log_2{n}+k^3)\) 或 \(O(\log_2{n}+k^2\log k)\)。
#include <bits/stdc++.h>
#define sz(x) (x.size())
using namespace std;
typedef long long ll;
const int N = 500005, K = 505, mod = 1e9 + 7, inv2 = mod + 1 >> 1;
typedef vector<int> Poly;
int n, k, st, fl, ans, pw[N], y[K], fac[K], inv[K], Inv[K]; Poly D[N]; char s[N];
inline int add(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
inline int dec(int x, int y) { return x - y < 0 ? x - y + mod : x - y; }
inline int mul(int x, int y) { return (ll)x * y % mod; }
inline int qpow(int a, int b = mod - 2) {
int ans = 1;
for (; b; b >>= 1, a = mul(a, a)) if (b & 1) ans = mul(ans, a);
return ans;
}
inline void precalc(int n) {
fac[0] = inv[0] = Inv[0] = fac[1] = inv[1] = Inv[1] = 1;
for (int i = 2; i <= n; ++ i)
fac[i] = mul(fac[i - 1], i),
Inv[i] = mul(mod - mod / i, Inv[mod % i]),
inv[i] = mul(inv[i - 1], Inv[i]);
}
inline ll C(int n, int m) { return mul(fac[n], mul(inv[m], inv[n - m])); }
inline Poly operator + (Poly f, Poly g) {
if (sz(f) < sz(g)) f.resize(sz(g));
for (int i = 0; i < sz(g); ++ i) f[i] = add(f[i], g[i]);
return f;
}
inline Poly operator - (Poly f, Poly g) {
if (sz(f) < sz(f)) f.resize(sz(g));
for (int i = 0; i < sz(g); ++ i) f[i] = dec(f[i], g[i]);
while (sz(f) && !f.back()) f.pop_back();
return f;
}
inline Poly PolyShift(Poly f, int delta) {
int deg = sz(f); Poly g(deg, 0);
static int csy[K]; csy[0] = 1;
for (int i = 1; i < deg; ++ i) csy[i] = mul(csy[i - 1], delta);
for (int j = 0; j < deg; ++ j)
for (int i = j; i < deg; ++ i)
g[j] = add(g[j], mul(mul(C(i, j), f[i]), csy[i - j]));
return g;
}
inline int calc(Poly f, int x) {
int ans = 0;
for (int i = sz(f) - 1; ~i; -- i) ans = add(mul(ans, x), f[i]);
return ans;
}
int main() {
scanf("%s", s), n = strlen(s);
scanf("%d", &k); Poly F; F.resize(k); precalc(k + 3);
for (int i = 0; i < k; ++ i) scanf("%d", &F[i]);
pw[0] = 1;
for (int i = 1; i <= n; ++ i) pw[i] = add(pw[i - 1], pw[i - 1]);
reverse(s, s + n);
D[0] = F;
for (int i = 1; i < n; ++ i) D[i] = D[i - 1] - PolyShift(D[i - 1], pw[i - 1]);
for (int i = n - 1; ~i; -- i) if (s[i] == '1') {
ans = fl & 1 ? add(ans, calc(D[i], st)) : dec(ans, calc(D[i], st));
st = add(st, pw[i]), fl ^= 1;
}
st = dec(st, 1);
if (st >= 0) {
for (int i = 0; i <= k + 1; ++ i)
y[i] = add(i ? y[i - 1] : 0, calc(F, i));
for (int i = 0; i <= k + 1; ++ i) {
int coef = y[i];
for (int j = 0; j <= k + 1; ++ j) if (i ^ j)
coef = mul(coef, mul(dec(st, j), qpow(dec(i, j))));
ans = add(ans, coef);
}
}
return printf("%d\n", mul(ans, inv2)), 0;
}