zoukankan      html  css  js  c++  java
  • 【洛谷4887】【模板】莫队二次离线

    点此看题面

    • 给定一个长度为(n)的序列以及一个常数(k)
    • (q)次询问,每次求一个区间内有多少对(i,j)满足(i<j)(a_ioplus a_j)二进制下恰有(k)(1)
    • (n,qle10^5,0le V<2^{14})

    二次离线莫队

    其实之前写过一次二次离线莫队,是求区间逆序对。感觉这道题要好写的多。

    就是考虑我们移动区间的复杂度比较大,但实际上我们一次把莫队右端点(R)移到当前询问的右端点(qr),答案的变化量就是(R+1)([L,R])(R+2)([L,R+1]),...,(qr)([L,qr-1])能产生的贡献。

    我们把这个贡献差分,就变成(R+1)([1,R]),...,(qr)([1,qr-1])的贡献和减去(R+1)([1,L-1]),...,(qr)([1,L-1])的贡献和。

    发现对于第一部分的贡献,区间的右端点都是询问点(-1),可以直接计算出所有的这类贡献。

    而对于第二部分的贡献,左端点相同,考虑直接把这个区间([R+1,qr])扔到(L-1)的一个(vector)里面,之后我们第二次离线,枚举每个前缀加入其中全部数处理对应(vector)中的区间询问(这里的区间询问可以直接暴枚区间中每个数一个一个询问,就相当于是莫队复杂度)。

    要计算加入一个数的贡献,发现二进制下恰有(k)(1)的数最多只有(C_{14}^7)个,而(a_ioplus a_j=x)显然等价于(a_ioplus x=a_j),所以要加入一个数的贡献只要枚举所有(k)(1)的数与它异或更新计数数组即可,然后询问就可以直接(O(1))调用了。

    其余四种端点移动也是类似的,就是要注意移动左端点的时候是用(L)([1,R])的贡献减去(L)([1,L])的贡献,端点不固定的那类区间的右端点是询问点而非询问点(-1),因此当(k=0)的时候要记得算上一个位置与自己产生的贡献。

    代码:(O(nsqrt n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define V 16384
    #define LL long long
    using namespace std;
    int n,m,k,a[N+5],sz,bl[N+5],s[V+5],c[V+5];LL f[N+5],g[N+5],ans[N+5];
    struct Q {int p,l,r;I bool operator < (Con Q& o) Con {return bl[l]^bl[o.l]?l<o.l:r<o.r;}}q[N+5];
    struct Q2 {int p,l,r,op;I Q2(CI i=0,CI a=0,CI b=0,CI c=0):p(i),l(a),r(b),op(c){}};vector<Q2> v[N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    I int Cnt(RI x) {RI t=0;W(x) x&=x-1,++t;return t;}
    int main()
    {
    	RI i;for(read(n,m,k),sz=sqrt(n),i=1;i<=n;++i) read(a[i]),bl[i]=(i-1)/sz+1;
    	RI t=0;for(i=0;i^V;++i) Cnt(i)==k&&(s[++t]=i);//预处理出所有恰有k个1的数
    	for(i=1;i<=m;++i) read(q[i].l,q[i].r),q[i].p=i;
    	RI L=1,R=0;for(sort(q+1,q+m+1),i=1;i<=m;++i)//莫队
    		R<q[i].r&&(v[L-1].push_back(Q2(q[i].p,R+1,q[i].r,-1)),R=q[i].r),
    		L>q[i].l&&(v[R].push_back(Q2(q[i].p,q[i].l,L-1,1)),L=q[i].l),
    		R>q[i].r&&(v[L-1].push_back(Q2(q[i].p,q[i].r+1,R,1)),R=q[i].r),
    		L<q[i].l&&(v[R].push_back(Q2(q[i].p,L,q[i].l-1,-1)),L=q[i].l);
    	RI j,p,vs;for(i=1;i<=n;++i) {for(f[i]=f[i-1]+c[a[i]],g[i]=f[i]+(k?0:i),j=1;j<=t;++j) ++c[a[i]^s[j]];//离线枚举前缀
    		for(j=0,vs=v[i].size();j^vs;++j) for(p=v[i][j].l;p<=v[i][j].r;++p) ans[v[i][j].p]+=v[i][j].op*c[a[p]];}//处理这个前缀上的区间询问
    	for(L=1,R=0,i=1;i<=m;++i) ans[q[i].p]+=ans[q[i-1].p]+g[q[i].l-1]-g[L-1]+f[q[i].r]-f[R],L=q[i].l,R=q[i].r;//统计贡献总和,注意算出的是答案变化量要给ans做前缀和
    	for(i=1;i<=m;++i) writeln(ans[i]);return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    转换进制,十六进制数相加
    一个人的旅行(Dijkstra算法)
    畅通工程续(Dijkstra算法)
    免费书下载
    http://d3js.org/
    React.js model
    jtable更新数据
    java knowledge record
    ActionListener三种实现
    java接口理解(转载)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4887.html
Copyright © 2011-2022 走看看