51nod2554 OR 三元组
51nod2554 OR 三元组
Description
给定长度为 \(n\) 的序列 \(a\),有 \(m\) 组询问,每次给定区间 \([l, r]\) 和整数 \(x\),求有多少三元组 \((i, j, k)\) 满足:
- \(l \le i < j < k \le r\)
- \(a_i | a_j | a_k = x\)
其中 \(x | y\) 代表按位或。
\(n, m \le 10^5, a_i, x < 2^8\)。
Solution
非常 sb 的题目。
对于每一个询问,首先求出区间内每个数出现的次数,然后子集卷积求一个 \(f(x) = \sum_{l \le i < j < k \le r} [a_i | a_j | a_k \subseteq x]\)(这里的 \(\subseteq\) 指前者为 \(1\) 的位后者均为 \(1\)),然后再 IFWT 即可得到答案序列。
时间复杂度 \(\mathcal{O}(nw + (3^8 + 2^{11})m)\),这里子集卷积暴力更优。跑得非常快。
#include <bits/stdc++.h>
using namespace std;
#define il inline
#define re register
#define rep(i, s, e) for (re int i = s; i <= e; ++i)
#define drep(i, s, e) for (re int i = s; i >= e; --i)
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
using ll = long long;
const int N = 100000 + 10;
const int W = 256;
il int read() {
int x = 0; bool f = true; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = false; c = getchar();}
while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? x : -x;
}
il void FWT(ll *f, int lim, int opt) {
for (int o = 2, k = 1; k < lim; o <<= 1, k <<= 1) {
for (int i = 0; i < lim; i += o) {
rep(j, 0, k - 1) f[i + j + k] += opt * f[i + j];
}
}
}
int n, m, a[N], sum[N][W], cnt[W];
ll f[W];
int main() {
n = read(), m = read();
rep(i, 1, n) {
a[i] = read();
rep(j, 0, W - 1) sum[i][j] = sum[i - 1][j];
++ sum[i][a[i]];
}
while (m --) {
int l = read(), r = read(), x = read();
rep(i, 0, W - 1) cnt[i] = sum[r][i] - sum[l - 1][i];
rep(i, 0, W - 1) {
f[i] = 0;
for (int s = i; ; s = i & (s - 1)) {
f[i] += cnt[s];
if (!s) break;
}
}
rep(i, 0, W - 1) f[i] = f[i] * (f[i] - 1) * (f[i] - 2) / 6;
FWT(f, W, -1);
printf("%lld\n", f[x]);
}
return 0;
}