1. 程式人生 > >BZOJ4939: [Ynoi2016]掉進兔子洞

BZOJ4939: [Ynoi2016]掉進兔子洞

BZOJ

題意

給你一個長度為 n n 的數列,每次詢問三個區間共有的數的個數;

題解

對於這種求交或求並的問題,往往會先考慮到使用bitset,但是本題的資料範圍太大需要離散化,且bitset無法記錄數量,這裡就只能在離散化的時候用一點小技巧了:對於重複出現的數我們不需要unique,假如某數第一次出現的編號為 x

x ,那麼第二次出現它的編號就可以設為 x + 1 x+1 ,以此類推,對於這道題,每個區間獨立出來考慮,某數在一區間中第一次出現就用它的 x
x
編號,第二次出現就用 x + 1 x+1 ,這樣就相當於把同樣的數看做了不同,bitset也就能發揮它的作用了;現在就有了這麼一個做法,如果能得到三個區間的數的集合,再 a
n d and
一下,就能知道共有的數的個數了,但是有多組詢問,該怎麼得到每一個詢問的三個區間的數集呢,考慮使用莫隊,每次區間擴大或縮小時我們都能只花費 O ( n 32 ) O(\frac{n}{32}) 的代價,就能再原區間的基礎上得到新的區間的數集,再把一個詢問看成三個區間分別詢問,最後再 a n d and 到一起,便能輕鬆解決本題了;但是一算空間,超了怎麼辦?把 Q Q 次詢問分三次來進行,這樣就只用一次性開 Q 3 \frac{Q}{3} 個bitset了,空間時間也容得下;但是注意離散化時不能加 l o g log ;

#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define RG register
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef long double LD;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
    char c = getchar();
    bool f = false;
    for (x = 0; !isdigit(c); c = getchar()) {
        if (c == '-') {
            f = true;
        }
    }
    for (; isdigit(c); c = getchar()) {
        x = x * 10 + c - '0';
    }
    if (f) {
        x = -x;
    }
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
    read(x), read(y...);
}
const int N=1e5+10;
int n;
int W[N],V[N],CT[N],part[N],ANS[N/3],S[N],M[N];
bitset<N> G,F[N/3];
struct Data {
    int l,r,id;
}Q[N];
bool cmp1(int A,int B) {
    return W[A]<W[B];
}
bool cmp2(Data A,Data B) {
    return part[A.l]==part[B.l]?A.r<B.r:part[A.l]<part[B.l];
}
void Move(int pos,int val) {
    int t=M[pos];
    if(val<0) G.reset(S[t]+CT[t]-1);
    CT[t]+=val;
    if(val>0) G.set(S[t]+CT[t]-1);
}
void Solve(int m) {
    if(!m) return;
    int cnt=0;
    for(int i=1;i<=m;++i) {
        F[i].set(); ANS[i]=0;
        for(int j=0;j<3;++j) ++cnt,read(Q[cnt].l,Q[cnt].r),Q[cnt].id=i,ANS[i]+=Q[cnt].r-Q[cnt].l+1;
    }
    sort(Q+1,Q+cnt+1,cmp2);
    G.reset(); mem(CT,0);
    for(int i=1,L=1,R=0;i<=cnt;++i) {
        while(R<Q[i].r) Move(++R,1);
        while(R>Q[i].r) Move(R--,-1);
        while(L<Q[i].l) Move(L++,-1);
        while(L>Q[i].l) Move(--L,1);
        F[Q[i].id]&=G;
    }
    for(int i=1;i<=m;++i) printf("%d\n",ANS[i]-3*(int)F[i].count());
}
//#define rua
int main() {
#ifdef rua
    freopen("GG.out","w",stdout);
#endif
    int m; read(n,m);
    int Base=sqrt(n+1),cnt=1,ID=1;
    for(int i=1;i<=n;++i) {
        read(W[i]); part[i]=ID;
        if(cnt==Base) cnt=1,++ID;
        else ++cnt;
        V[i]=i;
    }
    sort(V+1,V+n+1,cmp1);
    for(int i=1,t=0;i<=n;++i) {
        if(W[V[i]]!=W[V[i-1]]) ++t,S[t]=i;
        M[V[i]]=t;
    }
    Solve(min(33334,m));
    if(m>=33334) m-=33334;
    else m=0;
    Solve(min(33333,m));
    if(m>=33333) m-=33333;
    else m=0;
    Solve(min(33333,m));
    return 0;
}