BZOJ 4939 [Ynoi2016]掉进兔子洞(莫队+bitset)

Posted forever97‘s blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 4939 [Ynoi2016]掉进兔子洞(莫队+bitset)相关的知识,希望对你有一定的参考价值。

 

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=4939

 

【题目大意】

  给出一个数列,每个询问给出三个区间,问除去三个区间共有的数字外,
  还剩下几个数字,注意删去的是共有的数字个数,不是数字种类,统计时候也一样

 

【题解】

  首先,答案为区间长度和减去区间并数字个数和的三倍。
  所以题目转化为求区间并。很显然在开始对数据可以进行离散化。
  考虑每个数字只出现一次的情况,我们可以用bitset来统计区间某个数字是否存在,
  莫队处理查询每个区间,保存其bitset的值,最后求交即可,
  现在考虑每个数字出现多次的情况,
  我们发现经过离散的数据之间空位数量恰好可以用来标出现多次的数据,
  比如1 4 4 9 9,离散后为 1 2 2 4 4,
  我们可以将多出来的2标在3位置,4标在5位置,那么就可以用bitset统计了。
  - Me : 询问区间存不下怎么办?
  - Claris :将询问分批进行处理,单次处理25000个询问
  - Me : 超时了欸……
  - Claris : 这题卡常数,要手写bitset.
  - Me : 你的代码为什么有6.7k?
  - Claris :我分出现一次,两次和跟多次讨论
  - Me : 我……还是咸鱼吧……

 

【代码】

#include <cstdio>
#include <algorithm>
#include <bitset>
#include <cmath>
using namespace std;
typedef unsigned long long ULL;
const int N=100010,M=N<<2;
int limit,n,m,pos[N],a[N],cnt[N],Ans[N],mark[N];
struct Q{
    int l,r,id;
    friend bool operator < (const Q &a,const Q &b){
        return pos[a.l]<pos[b.l]||(pos[a.l]==pos[b.l]&&a.r<b.r);
    }
}ask[M];
int read(int &x){
    int f=1;char ch=getchar();x=0;
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    x*=f;
}
int disc[N];
int remark(int x){
    int l=1,r=n,res=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(disc[mid]<x)l=mid+1;
        else res=mid,r=mid-1;
    }return res;
}
const int B=1567,K=25000;
ULL v[B],f[K+3][B];
int u[65537],tmp,U;
void flip(int x){v[x>>6]^=1ULL<<(x&63);}
void Copy(ULL*a){
    int i=0;
    for(;i+13<=U;i+=14){
        for(int j=0;j<14;j++)a[i+j]=v[i+j];
    }for(;i<=U;i++)a[i]=v[i];
}
void And(ULL*a){
    int i=0;
    for(;i+13<=U;i+=14){
        for(int j=0;j<14;j++)a[i+j]&=v[i+j];
    }for(;i<=U;i++)a[i]&=v[i];
}
void popcount(ULL x){tmp+=u[x&65535]+u[x>>16&65535]+u[x>>32&65535]+u[x>>48];}
int count(ULL*a){
    int i=tmp=0;
    for(;i+13<=U;i+=14){
        for(int j=0;j<14;j++)popcount(a[i+j]);
    }for(;i<=U;i++)popcount(a[i]);
    return tmp;
}
void init(){for(int i=1;i<65536;i++)u[i]=u[i>>1]+(i&1);} 
int main(){
    read(n); read(m);
    U=n>>6; init();
    limit=(int)sqrt(n+0.5);
    for(int i=1;i<=n;i++)read(a[i]),disc[i]=a[i],pos[i]=(i-1)/limit+1;
    sort(disc+1,disc+n+1);
    for(int i=1;i<=n;i++)a[i]=remark(a[i]);
    //for(int i=1;i<=n;i++)printf("%d\n",a[i]); 
    int pos=0,l=1,r=0;
    while(pos<m){
        int tot=0;
        for(int i=1;i<=25000&&i+pos<=m;i++){
            tot+=3;
            Ans[i]=0;
            mark[i]=0;
            read(ask[i*3-2].l); read(ask[i*3-2].r); ask[i*3-2].id=i;
            read(ask[i*3-1].l); read(ask[i*3-1].r); ask[i*3-1].id=i;
            read(ask[i*3].l); read(ask[i*3].r); ask[i*3].id=i;
            Ans[i]+=ask[i*3-2].r-ask[i*3-2].l+1;
            Ans[i]+=ask[i*3-1].r-ask[i*3-1].l+1;
            Ans[i]+=ask[i*3].r-ask[i*3].l+1;
        }sort(ask+1,ask+tot+1);
        for(int i=1;i<=tot;i++){
            for(;r<ask[i].r;r++){flip(a[r+1]+cnt[a[r+1]]);cnt[a[r+1]]++;}
            for(;l>ask[i].l;l--){flip(a[l-1]+cnt[a[l-1]]);cnt[a[l-1]]++;}
            for(;l<ask[i].l;l++){cnt[a[l]]--;flip(a[l]+cnt[a[l]]);}
            for(;r>ask[i].r;r--){cnt[a[r]]--;flip(a[r]+cnt[a[r]]);}
            if(mark[ask[i].id])And(f[ask[i].id]);
            else Copy(f[ask[i].id]),mark[ask[i].id]=1;
        }tot/=3;
        for(int i=1;i<=tot;i++)printf("%d\n",Ans[i]-3*count(f[i])); 
        pos+=tot;
    }return 0;
}

以上是关于BZOJ 4939 [Ynoi2016]掉进兔子洞(莫队+bitset)的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj 4939][Ynoi 2016]掉进兔子洞

bzoj 4939: [Ynoi2016]掉进兔子洞

luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队

p4688 [Ynoi2016]掉进兔子洞

luogu P4688 [Ynoi2016]掉进兔子洞

luogu P4688 [Ynoi2016]掉进兔子洞