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;
}
posted @ 2017-07-31 12:56  forever97  阅读(467)  评论(0编辑  收藏  举报