P8569 [JRKSJ R6] 第七学区
首先,有若干种 \(O(n\log V)\) 的暴力。我们选择其中操作比较简单的一种:对于每一位,先求出所有 \(0\) 段的长度之和,然后用所有区间的长度之和减掉这个东西。枚举区间右端点位置 \(i\),设 \(p_j(0\le j<\log V)\) 表示当前在 \(j\) 这一位上极长 \(0\) 段的长度。再维护一个 \(s\) 表示 \(\sum_{j} p_j2^j\)。考虑当 \(i\) 右移时,设新增的数字是 \(x\),会发生什么变化:
- 若 \(x\) 的第 \(j\) 位是 \(0\),那么 \(s\gets s+2^j,p_j\gets p_j+1\);
- 否则,\(s\gets s-p_j2^j,p_j\gets 0\)。
考虑如何优化这个暴力。我们维护的无非是一个 \(\log V\times \log n\) 的矩阵,每次需要支持将某行加上 \(1\)(并处理进位)或者将某行整体置为 \(0\)。将这个矩阵转置,这样我们只需要维护 \(\log n\) 个 \([0,V)\) 间的数,对于加 \(1\) 操作可以模拟二进制加法器,对于置 \(0\) 操作,将每个数都与上(\(x\) 取反)即可。
时间复杂度 \(O(n\log n)\),且因为这个 \(\log n\) 的操作内只有若干次位运算,所以跑得很快。
READ::init(n);
ull ans = -(1LLU * (n + 1) * n / 2), sum = 0;
int tp = 0;
For(i, 1, n) {
if (i == (i & -i)) ++tp;
ull x = READ::read(), c = ~x;sum += c;
For(j, 0, tp) {
sum -= (x & val[j]) << j, val[j] &= ~x;
ull c2 = val[j] & c;val[j] ^= c, c = c2;
}
ans -= sum;
}
cout << ans << '\n';
标签:log,READ,杂题,sum,tp,ull,gets,2022.10
From: https://www.cnblogs.com/alan-zhao-2007/p/2022-10-problems.html