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\),根据子集反演有:
令 \(c=b-a\),两边同时乘以 \(s_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";
}
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/19069651

浙公网安备 33010602011771号