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\) 的二进制表示中的最高位将值反转,那么可以归纳推导出

\[b_t=\operatorname{popcount}(t)\operatorname{mod} 2 \]

最终的和式也就是

\[\sum\limits_{i=0}^{n-1}[\operatorname{popcount}(i)\operatorname{mod} 2=1]F(i) \]

考虑对求和的范围进行分段,分成若干长度为 \(2^t\) 的段进行计算。设

\[A_{t}(x)=\sum\limits_{i=x}^{x+2^{t}-1}[\operatorname{popcount}(i)\operatorname{mod} 2=1]F(i)\\ B_{t}(x)=\sum\limits_{i=x}^{x+2^{t}-1}[\operatorname{popcount}(i)\operatorname{mod} 2=0]F(i)\\ \]

以上式子中的 \(x\) 的二进制表示中 \(0\sim t-1\) 位为 \(0\)

进一步推导

\[\begin{aligned} A_{t+1}(x)&=\sum\limits_{i=x}^{x+2^{t+1}-1}[\operatorname{popcount}(i)\operatorname{mod} 2=1]F(i)\\ &=\sum\limits_{i=x}^{x+2^{t}-1}[\operatorname{popcount}(i)\operatorname{mod} 2=1]F(i)+\sum\limits_{i=x}^{x+2^{t}-1}[\operatorname{popcount}(i+2^{t})\operatorname{mod} 2=1]F(i+2^{t})\\ &=\sum\limits_{i=x}^{x+2^{t}-1}[\operatorname{popcount}(i)\operatorname{mod} 2=1]F(i)+\sum\limits_{i=x}^{x+2^{t}-1}[\operatorname{popcount}(i)\operatorname{mod} 2=0]F(i+2^{t})\\ &=A_{t}(x)+B_{t}(x+2^{t}) \end{aligned} \]

\(B_{t+1}(x)\) 也同理,于是有

\[A_{t+1}(x)=A_{t}(x)+B_{t}(x+2^{t})\\ B_{t+1}(x)=B_{t}(x)+A_{t}(x+2^{t})\\ \]

式子比较对称,尝试求和求差

\[A_{t+1}(x)+B_{t+1}(x)=\sum_{i=x}^{x+2^{t+1}-1}F(i)\\ A_{t+1}(x)-B_{t+1}(x)=(A_{t}(x)-B_{t}(x))-(A_{t}(x+2^{t})-B_{t}(x+2^{t})) \]

分段求和时,\(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_{t}(x)=C_{t-1}(x)-C_{t-1}(x+2^{t}) \]

乍一看直接从 \(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;
}
posted @ 2022-08-10 09:51  Samsara-soul  阅读(45)  评论(0编辑  收藏  举报