1. 程式人生 > >BZOJ 4939 [Ynoi2016]掉進兔子洞(莫隊+bitset)

BZOJ 4939 [Ynoi2016]掉進兔子洞(莫隊+bitset)

怎麽辦 分批 長度 get sqrt 開始 and 超時 --

【題目鏈接】 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)