• 【LuoguP4887】第十四分块(前体)


    题目链接

    题意

    区间两数异或在二进制下有 (k)(1) 的对数。

    Sol

    普通莫队的话,如果要实时维护好区间内的答案需要支持区间对一个数求答案。
    直接做不是很好做,容易发现其实这也就是一个区间询问。那么可以把莫队中要求的东西再次离线下来。
    我们把上述询问拆成前缀相减的形式,这样我们要做到就是多次询问一个前缀对一个数的答案。
    由于在数据范围下二进制下有 (k)(1) 的数并不是太多,我们可以直接从前往后做,遇到一个数 (x) 则把 (xoplus number(k)) 加入桶中 ((number(k))表示二进制下有 (k)(1)的数)。当我们遇到一个询问 数 (y) 和当前前缀的答案时,只需要看 (y) 的桶被加入了多少个数就行了。

    复杂度分析: 由于询问个数和莫队移动次数同阶,为 (O(nsqrt n)) ,查询复杂度是 (O(1)) 的所以查询复杂度就是 (O(nsqrt n))。然后我们每加入一个数需要 最多({14choose 7})次插入操作,所以这部分复杂度为 (O(n*{14choose 7}))

    愉快地提交上去后,就会 (TLE+MLE),因为这个复杂度还是比较紧的,而且询问个数 (O(nsqrt n)) 全存下来的话会被卡空间。
    怎么办呢?

    发现有许多询问都是一个前缀和前缀后面那一个数的答案,这部分我们可以直接一开始预处理然后询问时直接贡献。省去存储一些询问,加快了速度。

    但是这样还不够,再发现我们的另一种询问是对于一个固定的端点的询问一段区间内的数,我们可以只存一个询问。这样不仅省去了莫队时指针的移动复杂度,也省了空间,就可以过了。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    #define Set(a,b) memset(a,b,sizeof(a))
    template<class T>inline void init(T&x){
    	x=0;char ch=getchar();bool t=0;
    	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    	if(t) x=-x;return;
    }typedef long long ll;
    int n,m,k;
    const int N=1e5+10;
    int SIZE;
    const int INF=16384;
    int a[N],bl[N];ll ans[N];
    struct query{
    	int l,r,id;
    	query(){l=r=id=0;}
    	query(int _l,int _r,int _id){l=_l,r=_r,id=_id;}
    	inline bool operator <(const query b)const{if(bl[l]!=bl[b.l]) return bl[l]<bl[b.l];else return r<b.r;}
    }Que[N];
    typedef pair<int,int> PA;
    vector<query> PQ[N];
    int number[3434];int numcnt=0;
    #define lowbit(a) ((a)&(-a))
    inline int Count(int x){int cnt=0;while(x) ++cnt,x^=lowbit(x);return cnt;}
    int Pre[N],Bac[INF];ll S[N];
    
    int main()
    {
    	init(n),init(m),init(k);SIZE=sqrt(n);
    	for(int i=1;i<=n;++i) init(a[i]),bl[i]=(i-1+SIZE)/SIZE;
    	for(int i=0;i<INF;++i) if(Count(i)==k) number[++numcnt]=i;
    	
    	if(k>14) while(m--) puts("0");
    	else {
    		S[0]=0;
    		for(int i=1;i<=n;++i) {Pre[i]=Bac[a[i]];S[i]=S[i-1]+Pre[i];for(int j=1;j<=numcnt;++j) ++Bac[a[i]^number[j]];}S[n+1]=S[n];
    		Set(Bac,0);
    		for(int i=1;i<=m;++i) {init(Que[i].l),init(Que[i].r),Que[i].id=i;}sort(Que+1,Que+1+m);
    		int l=Que[1].l;int r=Que[1].l;
    		for(int i=1;i<=m;++i) {
    			const int ID=Que[i].id;
    			if(r<Que[i].r) {++r;ans[ID]+=S[Que[i].r]-S[r-1];PQ[l-1].emplace_back(query(r,Que[i].r,-ID));r=Que[i].r;}
    			if(l>Que[i].l) {--l;ans[ID]-=(S[l]-S[Que[i].l-1])+(int)(k==0)*(l-Que[i].l+1);PQ[r].emplace_back(query(Que[i].l,l,ID));l=Que[i].l;}
    			if(r>Que[i].r) {ans[ID]-=S[r]-S[Que[i].r];PQ[l-1].emplace_back(query(Que[i].r+1,r,ID));r=Que[i].r;}
    			if(l<Que[i].l) {ans[ID]+=S[Que[i].l-1]-S[l-1]+(int)(k==0)*(Que[i].l-l);PQ[r].emplace_back(query(l,Que[i].l-1,-ID));l=Que[i].l;}
    		}
    		Set(Bac,0);
    		for(int i=1;i<=n;++i) {
    			for(int j=1;j<=numcnt;++j) ++Bac[a[i]^number[j]];
    			for(query P:PQ[i]) {
    				int ID=abs(P.id),f=P.id/ID;
    				int l=P.l,r=P.r;ll res=0;
    				for(int j=l;j<=r;++j) res+=Bac[a[j]];
    				ans[ID]+=res*f;
    			}
    		}
    		for(int i=1;i<=m;++i) ans[Que[i].id]+=ans[Que[i-1].id];
    		for(int i=1;i<=m;++i) printf("%lld
    ",ans[i]);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    关于求LCA三种方法
    逆序对与本质不同的逆序对
    缩点+割点(tarjan)
    关于线段树
    引爆点
    0 基础认知产品经理
    一款 App 开发到上架
    坚持+时间管理
    测试流程
    Java学习笔记--字符串String、StringBuffer和StringBuilder
  • 原文地址:https://www.cnblogs.com/NeosKnight/p/11099224.html
Copyright © 2020-2023  润新知