题解:P8569 [JRKSJ R6] 第七学区

一些记号与约定

我们约定二进制表示下最低位为第 \(0\) 位,设 \(\operatorname{bit}_i(x)\)\(x\) 在二进制表示下第 \(i\) 位的值。

题意

给定长度为 \(n\) 的序列 \(a\),求其所有子区间的按位或和之和,答案对 \(2^{64}\) 取模。\(1\leq n\leq 5\times 10^7\)\(0\leq a_i<2^{64}\)

题解

神题。

容易得到一个 \(\mathcal{O}(n\log{V})\) 的做法。拆位,对于某一个位 \(i\),一个区间在该位上有贡献当且仅当其中存在 \(1\)。考虑从小到大右端点 \(i\),设 \(f_{j}\)\(a_{1\sim i}\) 在第 \(j\) 位中最后一个 \(1\) 的位置。每次加入一个 \(a_i\) 时,若 \(\operatorname{bit}_j(a_i)=1\),则令 \(f_j\gets i\),否则不变。每个 \(i\) 会对答案贡献 \(2^i\sum\limits_{j=0}^{\log{V}}f_j\)

考虑优化。这里使用一个很牛的 trick:注意到我们的转移在维护 \(f\) 时相当于维护了 \(\log{V}\)\(\leq n\) 的数,考虑把 \(f\) 视作一个 \(\log{V}\times \log{n}\)\(0/1\) 矩阵,那么我们将这个矩阵转置,改为维护 \(\log{n}\) 个 word。具体来说,维护 \(\log{n}\) 个 word \(g_i\),表示有哪些 \(j\) 使得 \(\operatorname{bit}_i(f_j)=1\)

考虑原来 \(f\) 的变化怎么放到 \(g\) 上。考察一个位 \(j\) 使得 \(\operatorname{bit}_j(a_i)=1\),对于每个 \(k\),我们需要把 \(g_k\) 的第 \(j\) 位设为 \(\operatorname{bit}_j(i)\)。不难看出这相当于:枚举 \(k\),若 \(\operatorname{bit}_k(i)=1\),则令 \(g_k\gets g_k\operatorname{or} a_i\),否则令 \(g_k\gets g_k\operatorname{and} \lnot a_i\)。此时每个 \(i\) 对答案贡献 \(\sum\limits_{j=0}^{\log{n}}2^jg_j\)

按照上述过程直接做即可,时间复杂度 \(\mathcal{O}(n\log{n})\)

代码
#include <bits/stdc++.h>

using namespace std;

#define lowbit(x) ((x) & -(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const int N = 5e7 + 5, B = 26;

template<typename T> inline void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> inline void chk_max(T &x, T y) { x = max(x, y); }

namespace IO {
	const int S = 1 << 24, lm = 1 << 23;
	char bi[S + 5], *p1 = bi, *p2 = bi, ch;
	#define gc() (p1 == p2 && (p2 = (p1 = bi) + fread(bi, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++)
	inline ull rd() {
		char ch;
		while (ch = gc(), (ch < '0'));
		ull x = ch ^ 48;
		while (ch = gc(), (ch >= '0')) x = (x << 3) + (x << 1) + (ch ^ 48);
		return x;
	}
}
using IO::rd;

namespace Read {
	int l, r;
	ull tp[10005], g1, g2;
	void init(int &n) {
		n = rd(), l = 1; int k = rd();
		for (int i = 1; i <= k; ++i) tp[i] = rd();
	}
	ull read() {
		if (l > r) l = rd(), r = rd(), g1 = rd(), g2 = rd();
		return tp[l++] * g1 + g2;
	}
}

int n;
ull ans, b[B];

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	Read::init(n);
	for (int i = 1; i <= n; ++i) {
		ull x = Read::read();
		for (int j = 0; j < B; ++j) ans += (i >> j & 1 ? b[j] |= x : b[j] &= ~x) << j;
	}
	cout << ans;
	return 0;
}
posted @ 2025-12-06 11:13  P2441M  阅读(30)  评论(0)    收藏  举报