【分块-莫队二次离线】LGP4887 第十四分块(前体)

【题目】
原题地址
给定一个序列 a a a,一个非负整数 k k k,和若干个询问 [ l , r ] [l,r] [l,r],问这段区间中满足 a i ⨁ a j 的 二 进 制 中 有 k 个 1 , 且 i &lt; j 的 数 对 数 a_i\bigoplus a_j的二进制中有k个1,且i&lt;j的数对数 aiajk1i<j
n , m ≤ 1 0 5 , a i &lt; 16384 n,m\leq 10^5,a_i&lt;16384 n,m105,ai<16384

【解题思路】
真是一道好(du)题啊!

考虑莫队,我们要求的实际上是一个位置对一个区间的贡献,这个显然是可以差分的。

现在只考虑右端点的移动,那么观察贡献的区间,总是某个位置 i i i [ 1 , l − 1 ] [1,l-1] [1,l1]的贡献以及 i i i对一个前缀 [ 1 , i − 1 ] [1,i-1] [1,i1]的贡献。

后者我们可以通过预处理得到。

对于前者,观察到右端点的移动总是连续的一段,那么我们可以在 l − 1 l-1 l1的位置 p u s h _ b a c k push\_back push_back一个区间,表示这段区间对这个前缀产生贡献。

由于区间总长是 n n n\sqrt n nn 的,所以我们现在只需要用一个支持 O ( 1 ) O(1) O(1)查询, O ( 3432 ) O(3432) O(3432)插入一个数字的东西就可以计算这部分的贡献,用一个数组就可以了。

左端点的移动类似,就是将对前缀的贡献改为对后缀的贡献即可。

时间复杂度 O ( 3432 ∗ n + n n ) O(3432*n+n\sqrt n) O(3432n+nn )

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define one(x) __builtin_popcount(x)
using namespace std;

typedef long long LL;
const int N=1e5+10,M=16384,lim=332;

int n,m,K,cnt;
int a[N],num[M],num2[M],kth[N];//cb1=1~i-1,cb2=1~i
LL ans[N],tans[N],cbl[N],cbr[N];

inline char Getchar()
{
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

bool Isdigit(char c){return (c>='0' && c<='9')?1:0;}
inline int read()
{
    int ret=0;char c=Getchar();
    while(!Isdigit(c)) c=Getchar();
    while(Isdigit(c)) ret=ret*10+(c^48),c=Getchar();
    return ret;
}

inline void write(LL x)
{
    if(x>9)write(x/10);
    putchar(x%10+'0');
}

struct Tquery{int l,r,bl,id;}q[N];
inline bool cmp(const Tquery&A,const Tquery&B)
{
    if(A.bl^B.bl) return A.l<B.l; 
    return (A.bl&1)?A.r<B.r:A.r>B.r;
}

struct Tnode
{
    int l,r,op,id;
    Tnode(){}
    Tnode(int _l,int _r,int _op,int _id):l(_l),r(_r),op(_op),id(_id){}
};
vector<Tnode>vall[N],valr[N];

inline int Min(int x,int y){return x<y?x:y;}
inline int Max(int x,int y){return x>y?x:y;}

void init()
{
    n=read();m=read();K=read();
    if(K>14) {for(int i=1;i<=m;++i)puts("0");exit(0);}
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=m;++i) 
        q[i].l=read(),q[i].r=read(),q[i].bl=(q[i].l-1)/lim+1,q[i].id=i;
    sort(q+1,q+m+1,cmp);
   
    for(int i=0;i<M;++i) if(one(i)==K) kth[++cnt]=i;
    
    memset(num,0,sizeof(num));memset(num2,0,sizeof(num2));
	for(int i=1,j=n;i<=n;++i,--j)
	{
		for(int k=1;k<=cnt;++k) cbr[i]+=num[a[i]^kth[k]],cbl[j]+=num2[a[j]^kth[k]];
		cbr[i]+=cbr[i-1];num[a[i]]++;cbl[j]+=cbl[j+1];num2[a[j]]++;
	}
}

void solve()
{
    q[0].l=1;q[0].r=0;
    for(int i=1;i<=m;++i)
    {
        if(q[i].r>q[i-1].r) 
        {
            valr[q[i-1].l-1].pb(Tnode(q[i-1].r+1,q[i].r,-1,i));
            ans[i]+=cbr[q[i].r]-cbr[q[i-1].r];
        }
        else if(q[i].r<q[i-1].r)
        {
            valr[q[i-1].l-1].pb(Tnode(q[i].r+1,q[i-1].r,1,i));
            ans[i]-=cbr[q[i-1].r]-cbr[q[i].r];
        }
        if(q[i].l<q[i-1].l) 
        {
            vall[q[i].r+1].pb(Tnode(q[i].l,q[i-1].l-1,-1,i));
            ans[i]+=cbl[q[i].l]-cbl[q[i-1].l];
        }
        else if(q[i].l>q[i-1].l)
        {
            vall[q[i].r+1].pb(Tnode(q[i-1].l,q[i].l-1,1,i));
            ans[i]-=cbl[q[i-1].l]-cbl[q[i].l];
        }
    }
    memset(num,0,sizeof(num));
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=cnt;++j) num[a[i]^kth[j]]++; 
        for(int j=0;j<(int)valr[i].size();++j)
        {
            int l=Max(1,valr[i][j].l),r=Min(n,valr[i][j].r),op=valr[i][j].op,id=valr[i][j].id;LL tmp=0;
            for(int k=l;k<=r;++k) tmp+=num[a[k]]; ans[id]+=tmp*op;
        }
    }
    memset(num,0,sizeof(num));
    for(int i=n;i;--i)
    {
        for(int j=1;j<=cnt;++j) num[a[i]^kth[j]]++;
        for(int j=0;j<(int)vall[i].size();++j)
        {
            int l=Max(1,vall[i][j].l),r=Min(n,vall[i][j].r),op=vall[i][j].op,id=vall[i][j].id;LL tmp=0;
            for(int k=l;k<=r;++k) tmp+=num[a[k]]; ans[id]+=tmp*op;
        }
    }
    for(int i=1;i<=m;++i) ans[i]+=ans[i-1];
    for(int i=1;i<=m;++i) tans[q[i].id]=ans[i];
    for(int i=1;i<=m;++i) write(tans[i]),putchar('\n');
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("LGP4887.in","r",stdin);
    freopen("LGP4887.out","w",stdout);
#endif
    init();
    solve();

    return 0;
}

【总结】
新科技get!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值