1. 程式人生 > >bzoj4939: [Ynoi2016]掉進兔子洞 莫隊 bitset

bzoj4939: [Ynoi2016]掉進兔子洞 莫隊 bitset

bzoj4939: [Ynoi2016]掉進兔子洞

Description

一個長為 n 的序列 a。
有 m 個詢問,每次詢問三個區間,把三個區間中同時出現的數一個一個刪掉,問最後三個區間剩下的數的個數和,詢問獨立。
注意這裡刪掉指的是一個一個刪,不是把等於這個值的數直接刪完,
比如三個區間是 [1,2,2,3,3,3,3] , [1,2,2,3,3,3,3] 與 [1,1,2,3,3],就一起扔掉了 1 個 1,1 個 2,2 個 3。

Input

第一行兩個數表示 n , m。
第二行 n個數表示 a[i]。
之後 m 行,每行 6 個數 l1 , r1 , l2, r2 , l3 , r3 表示這三個區間。

Output

對於每個詢問,輸出一個數表示答案。

Sample Input

5 2
1 2 2 3 3
1 2 2 3 3 4
1 5 1 5 1 5

Sample Output

3
0

HINT

n , m <= 100000 , 1 <= a[i] <= 1000000000

分析

一個很神仙的做法。
A n s =

l e n 3 m a x (
c n t 1 , c n t 2 , c n t 3 ) Ans=\sum len-3\sum max(cnt_1,cnt_2,cnt_3)
就是用長度減去公共部分。
考慮 b i t s e t bitset
但是 b i t s e t bitset 只能維護有多少相同的數,不能維護有多少公共的數。
有一種神奇的方法。離散化的時候定義每個數的權值為小於等於這個數的數的個數。
把某個數 p p 放入 b i t s e t bitset 裡面的時候,假設之前這個數放了 c n t [ p ] cnt[p] 個,把這個數放在 b i t s e t bitset p c n t [ p ] p-cnt[p] 位置上。
這樣的做法顯然是正確的。因為放了多少個數,在那個數的區間內就會有多少個字尾1
然後就用莫隊+bitset。
空間顯然是開不下的,所以拆成三次,重複做三遍莫隊即可。
時間複雜度 O ( n n + n 2 64 ) O(n\sqrt n+\frac{n^2}{64}) ,空間複雜度 O ( n 2 64 ) O(\frac{n^2}{64})

程式碼

手寫 b i t s e t bitset 真好玩~

#include<bits/stdc++.h>
const int M = 34010, N = 1e5 + 10;
typedef unsigned long long ULL;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
ULL All = 0;
int fs[65536], cnt[N], A[N], a[N], b[N], tot = 1, n, m, tp, B;
struct Bit {
    ULL s[1564];
    void Ini() {for(int i = 0;i <= 1563; ++i) s[i] = All;}
    void operator &= (Bit a) {for(int i = 0;i <= 1563; ++i) s[i] &= a.s[i];}
    void Add(int p) {s[p >> 6] ^= (ULL)1 << (p & 63);}
    int Cnt() {
        int r = 0;
        for(int i = 0;i <= 1563; ++i)
            for(ULL x = s[i]; x; x >>= 16)
                r += fs[x & 65535];
        return r;
    }
}c[M], nw;
struct Que {
    int l, r, id;
    void in(int i) {l = ri(); r = ri(); id = i;}
}q[M * 3];
void Add(int v, int p) {
    if(!~p) cnt[v] += p;
    nw.Add(v - cnt[v]);
    if(~p) cnt[v] += p;
}
bool cmp(Que a, Que b) {
    int x = a.l / B, y = b.l / B;
    return x == y ? ((x & 1) ? a.r < b.r : a.r > b.r) : x < y;
}
void Solve() {
    if(tot > m) return ;
    for(int i = 1;i <= M - 10 && tot <= m; ++i, ++tot)
        for(int k = 3; k--; ) 
            q[++tp].in(i), A[i] += q[tp].r - q[tp].l + 1;
    for(int i = 1;i <= tp / 3; ++i) c[i].Ini();
    B = sqrt(tp); std::sort(q + 1, q + tp + 1, cmp);
    int L = 1, R = 0;
    for(int i = 1;i <= tp; ++i) {
        for(;R < q[i].r;) Add(a[++R], 1);
        for(;L > q[i].l;) Add(a[--L], 1);
        for(;L < q[i].l;) Add(a[L++], -1);
        for(;R > q[i].r;) Add(a[R--], -1);
        c[q[i].id] &= nw;
    }
    for(int i = L;i <= R; ++i) Add(a[i], -1);
    for(int i = 1;i <= tp / 3; ++i) printf("%d\n", A[i] - c[i].Cnt() * 3), A[i] = 0; tp = 0;
}
int F(int x) {
    if(x > b[n]) return n + 1;
    int L = 1, R = n, m;
    for(;L != R; b[m = L + R >> 1] >= x ? R = m : L = m + 1) ;
    return L;
}
int main() {
    for(int i = 0;i < 64; ++i) All |= (ULL)1 << i;
    for(int i = 1;i < 65536; ++i) fs[i] = fs[i ^ (i&-i)] + 1;
    n = ri(); m = ri();
    for(int i = 1;i <= n; ++i) b[i] = a[i] = ri();
    std::sort(b + 1, b + n + 1);
    for(int i = 1;i <= n; ++i) a[i] = F(a[i] + 1) - 1;
    Solve(); Solve(); Solve();
    return 0;
}