Loading

2022.10 杂题

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';
posted @ 2022-10-14 21:10  Alan_Zhao_2007  阅读(46)  评论(1编辑  收藏  举报