1. 程式人生 > >Luogu4887 第十四分塊(前體)

Luogu4887 第十四分塊(前體)

sto l x l lxl orz

考慮莫隊,每次移動端點,我們都要詢問區間內和當前數字異或有 k k

1 1 的數字個數

詢問 [ l , r ]

[l,r] 可以再次離線,拆成詢問 [ 1 , l 1 ]
[1,l-1]
[ l , r ] [l,r]

然後考慮莫隊要移動 [ l , r ] [l,r] l l p p

假設 p > l p>l

那麼相當於每次詢問 a [ l ] a[l] [ l + 1 , r ] [l+1,r] ,然後 + + l ++l 直到 l = p l=p

即每次詢問 a [ l ] a[l] [ 1 , l ] [1,l] a [ l ] a[l] [ 1 , r ] [1,r]
對於前面的部分,它每次都是字首區間的最後一個數字詢問字首區間,可以預處理

對於後面的部分,它每次都是一個數字詢問一個固定的區間,直接在 r r 處打上一個詢問 l , p l,p 的標記,之後離線暴力詢問 l , p l,p ,這一部分複雜度和莫隊一樣

然後其它移動端點的方法類似

大力討論一下即可

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

namespace IO {
	const int maxn(1 << 21 | 1);

	char ibuf[maxn], *iS, *iT, c;
	int f;

	inline char Getc() {
		return iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++;
	}
	
	template <class Int> inline void In(Int &x) {
		for (f = 1, c = Getc(); c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
		for (x = 0; c >= '0' && c <= '9'; c = Getc()) x = x * 10 + (c ^ 48);
		x *= f;
	}
}

using IO :: In;

const int maxn(2e5 + 5);

int cnt, v[maxn], n, m, k, a[maxn], sum[maxn], blo;
ll cur, ret[maxn], ans[maxn], pre1[maxn], pre2[maxn];

struct Qry {
	int l, r, id;

	inline bool operator < (Qry b) const {
		return l / blo != b.l / blo ? l < b.l : r < b.r;
	}
} qry[maxn];

vector <Qry> q[maxn];

# define pk push_back

int main() {
	In(n), In(m), In(k), blo = sqrt(n);
	for (int i = 0; i < 16384; ++i) {
		int x = i, c = 0;
		for (; x; x ^= x & -x) ++c;
		if (c == k) v[++cnt] = i;
	}
	for (int i = 1; i <= n; ++i) In(a[i]);
	for (int i = 1; i <= m; ++i) In(qry[i].l), In(qry[i].r), qry[i].id = i;
	sort(qry + 1, qry + m + 1);
	for (int i = 1, l = qry[1].r + 1, r = qry[1].r; i <= m; ++i) {
		if (l < qry[i].l) q[r].pk((Qry){l, qry[i].l - 1, qry[i].id << 1});
		else if (l > qry[i].l) q[r].pk((Qry){qry[i].l, l - 1, qry[i].id << 1});
		l = qry[i].l;
		if (r < qry[i].r) q[l - 1].pk((Qry){r + 1, qry[i].r, qry[i].id << 1 | 1});
		else if (r > qry[i].r) q[l - 1].pk((Qry){qry[i].r + 1, r, qry[i].id << 1 | 1});
		r = qry[i].r;
	}
	for (int i = 1; i <= n; ++i) {
		pre1[i] = pre1[i - 1] + sum[a[i]];
		for (int j = 1; j <= cnt; ++j) ++sum[a[i] ^ v[j]];
		pre2[i] = pre2[i - 1] + sum[a[i]];
		for (auto t : q[i]) for (int j = t.l; j <= t.r; ++j) ret[t.id] += sum[a[j]];
	}
	for (int i = 1, l = qry[1].r + 1, r = qry[1].r; i <= m; ++i) {
		if (l < qry[i].l) cur += pre2[qry[i].l - 1] - pre2[l - 1] - ret[qry[i].id << 1];
		else if (l > qry[i].l) cur += ret[qry[i].id << 1] - pre2[l - 1] + pre2[qry[i].l - 1];
		l = qry[i].l;
		if (r < qry[i].r) cur += pre1[qry[i].r] - pre1[r] - ret[qry[i].id << 1 | 1];
		else if (r > qry[i].r) cur += ret[qry[i].id << 1 | 1] - pre1[r] + pre1[qry[i].r];
		ans[qry[i].id] = cur, r = qry[i].r;
	}
	for (int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
	return 0;
}