P6562 [SBCOI2020] 归家之路

定义两个正整数 \(x,y\)\(\subseteq\) 运算的结果为 (x&y)=x,也就是将每个二进制位看作一个元素之后集合 \(x\)\(y\) 的子集。

给定一个长为 \(2^n\) 的序列 \(v\),进行 \(q\) 次两种操作之一。

  • 给定 \(a\)\(b\)\(k\),对于每个 \(a\subseteq c \subseteq b\)\(a_c \leftarrow a_c+k\)
  • 给定 \(a\)\(b\),对于所有 \(a\subseteq c \subseteq b\),求 \(\sum a_c\)

\(1 \le n \le 16,1 \le q \le 2\times 10^5\)


首先叉掉 \(a \nsubseteq b\) 的情况。

显然有暴力做法,暴力枚举 \(b-a\) 的子集,时间复杂度 \(\mathcal O(2^{|b|-|a|})\)

接下来,令 \(s_i=\sum_{j\subseteq i} v_j\),根据子集反演有:

\[v_a=\sum_{i\subseteq a}(-1)^{|a|-|i|}s_i \]

\(c=b-a\),两边同时乘以 \(s_c\)

\[\sum_{a\subseteq i\subseteq b}v_i=v_as_c=\sum_{i\subseteq a}(-1)^{|a|-|i|}s_{i+c} \]

只需要预处理 \(s\),即可在 \(\mathcal O(2^{|a|})\) 的时间内解决。

此时我们结合上面两个方法,就可以在至多 \(O \left(\frac{|b|} 2 \right)\) 的时间内解决查询。

对于修改,我们不妨取阈值 \(B\),然后定期重构,查询的时候暴力算贡献。

重构可以用和查询相似的方法,然后做高维前缀和,复杂度 \(\mathcal O\left(\frac qB2^n+q\sqrt{2^n}\right)\)

总的复杂度: \(\mathcal O \left(q\sqrt{2^n}+qB+\frac qBn2^n\right)\)

\(B=\sqrt{n2^n}\),此时复杂度为 \(\mathcal O \left(q\sqrt{n2^n}\right)\),可以通过。

拿下最优解,哈哈

#include <algorithm>
#include <iostream>
#include <vector>
#include <tuple>
using std::cin, std::cout;
const int M = 1 << 16;
typedef unsigned Ux;

int n, m, q, popc[M], rev[M];
Ux v[M], s[M], t[M];

auto brute_force = [](int a, int b, auto&& f) {
	int c = a ^ b;
	for(int t = c; t; --t &= c)
		f(a|t);
	f(a);
};

auto clever_way = [](int a, int b, auto&& f) {
	for(int t = a; t; --t &= a)
		f(b^t, popc[t] & 1 ? -1 : 1);
	f(b, 1);
};

std::vector<std::tuple<int, int, Ux>> modifies;

auto doPrefix = [](Ux* f, auto&& g) {
	for(int i = 0, t = 1; i < n; ++i, t *= 2) 
		for(int j = 0; j < m; ++j)
			if(j >> i & 1) g(f[j], f[j^t]);
};

Ux query(int a, int b) {
	if((a & b) != a) return 0;
	Ux sum = 0;
	if(popc[a^b] <= popc[a])
		brute_force(a, b, [&](int x) {sum += v[x];});
	else clever_way(a, b, [&](int x, Ux f) {sum += f * s[x];});
	for(auto& [x, y, z]: modifies) {
		int _x = x | a, _y = y & b;
		if((_x & _y) == _x) sum += (1u << popc[_x^_y]) * z;
	}
	return sum;
}

void rebuild() {
	for(int i = 0; i < m; ++i) t[i] = 0;
	for(auto& [x, y, z]: modifies) {
		if(popc[x^y] <= popc[x])
			brute_force(x, y, [&](int x) {v[x] += z;});
		else clever_way(x, y, [&](int x, Ux f) {t[x] += f * z;});
	}
	doPrefix(t, [](Ux& a, Ux& b) {b += a;});
	for(int i = 0; i < m; ++i) v[i] += t[i], s[i] = v[i];
	doPrefix(s, [](Ux& a, Ux& b) {a += b;});
}

const int T = 3000;

void modify(int a, int b, Ux k) {
	if((a & b) != a) return ;
	modifies.emplace_back(a, b, k);
	if(modifies.size() >= T)
		rebuild(), modifies.clear();
}

inline void init() {
	popc[0] = 0, rev[0] = 0;
	for(int i = 1; i < m; ++i) popc[i] = popc[i>>1] + (i & 1);
	for(int i = 1; i < m; ++i) rev[i] = rev[i>>1] >> 1 | (i & 1) << (n - 1);
	modifies.reserve(T);
}

int main() {
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n >> q, m = 1 << n;
	init();
	for(int i = 0; i < m; ++i)
		cin >> v[i], s[i] = v[i];
	doPrefix(s, [](Ux& a, Ux& b) {a += b;});
	for(int a, x, y, z; q--; ) {
		cin >> a >> x >> y;
		if(a == 1) {
			cin >> z, modify(x, y, z);
		} else cout << query(x, y) << "\n";
	}
}
posted @ 2025-09-02 11:20  CuteNess  阅读(5)  评论(0)    收藏  举报