【洛谷P4887】【模板】莫队二次离线(第十四分块(前体))

题目

题目链接:https://www.luogu.com.cn/problem/P4887
珂朵莉给了你一个序列 \(a\),每次查询给一个区间 \([l,r]\),查询 \(l \leq i< j \leq r\),且 \(a_i \oplus a_j\) 的二进制表示下有 \(k\) 个 \(1\) 的二元组 \((i,j)\) 的个数。\(\oplus\) 是指按位异或。
\(n,m\leq 10^5\),\(0\leq a_i<2^{14}\)。

思路

按照莫队二次离线的套路后需要支持的是 \(O(n\sqrt{n})\) 个形如 \([1,i]\) 中有多少个数与 \(j\) 异或的二进制表示下恰好有 \(k\) 个 \(1\)。
注意到 \(a\leq 2^{14}\),也就是说,二进制表示下恰好有 \(k\) 个 \(1\) 的数量,最大值为 \(k=7\) 时 \(\binom{14}{7}=3432\) 个。
预处理出所有二进制表示下有 \(k\) 个 \(1\) 的数,依次处理所有二次离线后的询问,开一个桶记录当 \(a_j=x\) 时,\(1\sim i\) 有多少个数与 \(x\) 异或起来二进制下有 \(k\) 个 \(1\)。当 \(i\) 增加时,桶最多更新 \(3432\) 个位置。
时间复杂度 \(O(n\sqrt n+3432 n)\)。空间复杂度 \(O(n+m)\)。

代码

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

const int N=100010,B=320;
int n,m,k,Q,a[N],b[N],bel[N];
ll ans[N],sum1[N],sum2[N],cnt1[N],cnt2[N];

struct node
{
	int l,r,id;
}ask[N];
vector<node> L[N],R[N]; 

bool cmp(node x,node y)
{
	if (bel[x.l]!=bel[y.l]) return x.l<y.l;
	return x.r<y.r;
}

int main()
{
	scanf("%d%d%d",&n,&Q,&k);
	for (int i=0;i<16384;i++)
	{
		int cnt=0;
		for (int j=0;j<=14;j++)
			if (i&(1<<j)) cnt++;
		if (cnt==k) b[++m]=i;
	}
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum1[i]=sum1[i-1]+cnt1[a[i]];
		for (int j=1;j<=m;j++) cnt1[a[i]^b[j]]++;
		bel[i]=(i-1)/B+1;
	}
	for (int i=n;i>=1;i--)
	{
		sum2[i]=sum2[i+1]+cnt2[a[i]];
		for (int j=1;j<=m;j++) cnt2[a[i]^b[j]]++;
	}
	memset(cnt1,0,sizeof(cnt1));
	memset(cnt2,0,sizeof(cnt2));
	for (int i=1;i<=Q;i++)
	{
		scanf("%d%d",&ask[i].l,&ask[i].r);
		ask[i].id=i;
	}
	sort(ask+1,ask+1+Q,cmp);
	for (int i=1,l=1,r=0;i<=Q;i++)
	{
		int id=ask[i].id;
		if (l>ask[i].l)
		{
			ans[id]+=sum2[ask[i].l]-sum2[l];
			R[r+1].push_back((node){ask[i].l,l-1,-id}); l=ask[i].l;
		}
		if (r<ask[i].r)
		{
			ans[id]+=sum1[ask[i].r]-sum1[r];
			L[l-1].push_back((node){r+1,ask[i].r,-id}); r=ask[i].r;
		}
		if (l<ask[i].l)
		{
			ans[id]-=sum2[l]-sum2[ask[i].l];
			R[r+1].push_back((node){l,ask[i].l-1,id}); l=ask[i].l;
		}
		if (r>ask[i].r)
		{
			ans[id]-=sum1[r]-sum1[ask[i].r];
			L[l-1].push_back((node){ask[i].r+1,r,id}); r=ask[i].r;
		}
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++) cnt1[a[i]^b[j]]++;
		for (int j=0;j<(int)L[i].size();j++)
		{
			int id=abs(L[i][j].id),f=(L[i][j].id>0)?1:-1;
			for (int l=L[i][j].l;l<=L[i][j].r;l++)
				ans[id]+=f*cnt1[a[l]];
		}
	}
	for (int i=n;i>=1;i--)
	{
		for (int j=1;j<=m;j++) cnt2[a[i]^b[j]]++;
		for (int j=0;j<(int)R[i].size();j++)
		{
			int id=abs(R[i][j].id),f=(R[i][j].id>0)?1:-1;
			for (int l=R[i][j].l;l<=R[i][j].r;l++)
				ans[id]+=f*cnt2[a[l]];
		}
	}
	for (int i=1;i<=Q;i++) ans[ask[i].id]+=ans[ask[i-1].id];
	for (int i=1;i<=Q;i++) cout<<ans[i]<<"\n";
	return 0;
}
上一篇:数据库设计(MySQL)


下一篇:CCF数组推导-202109-1