zoukankan      html  css  js  c++  java
  • 【洛谷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,mleq 10^5)(0leq a_i<2^{14})

    思路

    按照莫队二次离线的套路后需要支持的是 (O(nsqrt{n})) 个形如 ([1,i]) 中有多少个数与 (j) 异或的二进制表示下恰好有 (k)(1)
    注意到 (aleq 2^{14}),也就是说,二进制表示下恰好有 (k)(1) 的数量,最大值为 (k=7)(inom{14}{7}=3432) 个。
    预处理出所有二进制表示下有 (k)(1) 的数,依次处理所有二次离线后的询问,开一个桶记录当 (a_j=x) 时,(1sim i) 有多少个数与 (x) 异或起来二进制下有 (k)(1)。当 (i) 增加时,桶最多更新 (3432) 个位置。
    时间复杂度 (O(nsqrt 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]<<"
    ";
    	return 0;
    }
    
  • 相关阅读:
    使用公用表表达式(CTE)简化嵌套SQL WITH AS的含义
    C#中Array与ArrayList的区别
    Asp.net 网页中的嵌入式代码
    Asp.net核心对象
    Latex学习(载入图片并居中)
    matlab练习程序(生成加密p文件)
    matlab练习程序(直方图反向投影)
    matlab练习程序(非负矩阵分解)
    matlab练习程序(PSNR)
    matlab练习程序(动感模糊)
  • 原文地址:https://www.cnblogs.com/stoorz/p/15366417.html
Copyright © 2011-2022 走看看